設計模式之代理模式

什麼是代理模式

代理模式就是為一個對象提供一個代理對象,由這個代理對象控制對該對象的訪問。

理解代理模式,可以對照生活中的一些具體例子,比如房產中介、二手車交易市場、經紀人等。

為什麼要用代理模式

通過使用代理模式,我們避免了直接訪問目標對象時可能帶來的一些問題,比如:遠程調用,需要使用遠程代理來幫我們處理一些網絡傳輸相關的細節邏輯;可能需要基於某種權限控制對目標資源的訪問,可以使用保護代理等。

總的來說,通過是用代理模式,我們可以控制對目標對象的訪問,可以在真實方法被調用前或調用后,通過代理對象加入額外的處理邏輯。

代理模式分類

代理模式分為靜態代理和動態代理。動態代理根據實現不同又可細分為JDK動態代理和cglib動態代理。

靜態代理是由程序員創建或工具生成代理類的源碼,再編譯代理類。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委託類的關係在運行前就確定了。

動態代理是在實現階段不用關心代理類,而在運行時動態生成代理類的。

靜態代理

以房哥買房子為例,用代碼實現靜態代理。

1、首先建立一個Seller接口

public interface Seller {
    void sell();
}

2、創建實現類,房哥,有一個方法,就是買房子

public class FangGe implements Seller{
    @Override
    public void sell() {
        System.out.println("房哥要出手一套四合院");
    }
}

3、買房子需要找到買家,達成交易后還要辦理過戶等其他手續,房哥只想賣房收錢就完了。因此,需要找一個代理來幫房哥處理這些雜事。

我們創建一個代理類FangGeProxy,代理類也需要實現Seller接口,行為上要保持和FangGe一樣,都是要賣房子。同時該代理類還需要持有房哥的引用。

public class FangGeProxy implements Seller{
    private FangGe fangGe;

    public FangGeProxy(FangGe fangGe){
        this.fangGe = fangGe;
    }
    @Override
    public void sell() {
        findBuyer();
        fangGe.sell();
        afterSell();
    }
    
    public void findBuyer(){
        System.out.println("代理幫助尋找買主");
    }
    
    public void afterSell(){
        System.out.println("達成交易后,辦理相關手續");
    }
}

可以看到,房哥的代理類通過findBuyer()和afterSell()兩個方法幫助房哥完成了其他一些雜事。

4、測試類

public class StaticProxyTest {
    public static void main(String[] args) {
        Seller seller = new FangGeProxy(new FangGe());
        seller.sell();
    }
}

輸出:

代理幫助尋找買主
房哥要出手一套四合院
達成交易后,辦理相關手續

最後,看下類圖

靜態代理的問題:

1、由於靜態代理類在編譯前已經確定了代理的對象,因此靜態代理只能代理一種類型的類,如果要給大量的類做代理,就需要編寫大量的代理類;

2、如果我們要給Seller,也就是目標對象要增加一些方法,則需要同步修改代理類,不符合開閉原則。

JDK動態代理

JDK的動態代理依賴於jdk給我們提供的類庫實現,是一種基於接口實現的動態代理,在編譯時並不知道要代理哪個類,而是在運行時動態生成代理類。同時也解決了靜態代理中存在的問題。

我們接上上面靜態代理的例子,繼續實現JDK的動態代理。

1、我們建一個方法轉發的處理器類,該類需要實現InvocationHandler接口。

public class SellerInvocationHandler implements InvocationHandler {

    // 要代理的真實對象
    private Object target;

    /**
     * 使用Proxy類靜態方法獲取代理類實例
     */
    public Object getProxyInstance(Object target){
        this.target = target;
        Class<?> clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object obj = method.invoke(this.target, args);
        after();
        return obj;
    }

    private void before() {
        System.out.println("執行方法前");
    }
    
    private void after() {
        System.out.println("執行方法后");
    }
}

2、新建JDK動態代理測試類,首先代理房哥賣房子

public class JDKDynamicProxyTest {
    public static void main(String[] args) {

        // new一個房哥,下面幫房哥找個代理
        FangGe fangGe = new FangGe();
        SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();
        
        // 房哥的代理對象
        Seller seller = (Seller) sellerInvocationHandler.getProxyInstance(fangGe);
        seller.sell();

    }
}

輸出:

執行方法前
房哥要出手一套四合院
執行方法后

可以看到,完成了代理。

3、接下來我們新建另外一個類,User類,並使用JDK動態代理完成代理User類

public interface IUser {
    void sayHello();

    void work();
}

public class UserImpl implements IUser{
    @Override
    public void sayHello() {
        System.out.println("hello,我是小明");
    }

    @Override
    public void work() {
        System.out.println("我正在寫代碼");
    }
}

修改測試類,

public class JDKDynamicProxyTest {
    public static void main(String[] args) {

/*        // new一個房哥,下面幫房哥找個代理
        FangGe fangGe = new FangGe();
        SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();

        // 房哥的代理對象
        Seller seller = (Seller) sellerInvocationHandler.getProxyInstance(fangGe);
        seller.sell();*/

        // 代理user類
        IUser user = new UserImpl();
        SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();
        IUser userProxy = (IUser) sellerInvocationHandler.getProxyInstance(user);
        userProxy.sayHello();
        userProxy.work();

    }
}

輸出:

執行方法前
hello,我是小明
執行方法后
執行方法前
我正在寫代碼
執行方法后

可以看到,我們SellerInvocationHandler 並未做任何改動,它便能為UserImpl類生成代理,並在執行方法的前後增加額外的執行邏輯。

cglib動態代理

JDK動態代理有一個局限就是,被代理的類必須要實現接口。如果被代理的類沒有實現接口,則JDK動態代理就無能為力了。這個時候該cglib動態代理上場了。

CGLIB是一個功能強大,高性能的代碼生成包。它為沒有實現接口的類提供代理,為JDK的動態代理提供了很好的補充。通常可以使用Java的動態代理創建代理,但當要代理的類沒有實現接口或者為了更好的性能,CGLIB是一個好的選擇。

1、新建一個MyCglibInterceptor,實現MethodInterceptor接口。該類類似於JDK動態代理中的InvocationHandler實例,是實現cglib動態代理的主要類。

public class MyCglibInterceptor implements MethodInterceptor {

    public Object getCglibProxyInstance(Object object){
        // 相當於Proxy,創建代理的工具類
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(object.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object obj = methodProxy.invokeSuper(o, objects);
        after();
        return obj;
    }

    private void before() {
        System.out.println("執行方法之前");
    }

    private void after() {
        System.out.println("執行方法之後");
    }
}

2、新建cglib動態代理的測試類,先代理上面例子中的User類。

public class CglibDynamicProxyTest {
    public static void main(String[] args) {
        MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor();
        IUser userCglibProxy = (IUser) myCglibInterceptor.getCglibProxyInstance(new UserImpl());
        userCglibProxy.sayHello();
        userCglibProxy.work();
    }
}

輸出:

執行方法之前
hello,我是小明
執行方法之後
執行方法之前
我正在寫代碼
執行方法之後

3、新建一個類HelloWorld,不實現任何接口,為該類實現動態代理。

public class HelloWorld {
    public void hello(){
        System.out.println("世界這麼大,我想去看看");
    }
}

測試代理類

public class CglibDynamicProxyTest {
    public static void main(String[] args) {
/*        MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor();
        IUser userCglibProxy = (IUser) myCglibInterceptor.getCglibProxyInstance(new UserImpl());
        userCglibProxy.sayHello();
        userCglibProxy.work();*/

        // 代理未實現任何接口的類
        MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor();
        HelloWorld helloWorldProxy = (HelloWorld) myCglibInterceptor.getCglibProxyInstance(new HelloWorld());
        helloWorldProxy.hello();
    }
}

輸出:

執行方法之前
世界這麼大,我想去看看
執行方法之後

使用cglib動態代理,我們實現了對普通類的代理。

(完)

設計模式系列文章

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

將Swagger2文檔導出為HTML或markdown等格式離線閱讀

網上有很多《使用swagger2構建API文檔》的文章,該文檔是一個在線文檔,需要使用HTTP訪問。但是在我們日常使用swagger接口文檔的時候,有的時候需要接口文檔離線訪問,如將文檔導出為html、markdown格式。又或者我們不希望應用系統與swagger接口文檔使用同一個服務,而是導出HTML之後單獨部署,這樣做保證了對接口文檔的訪問不影響業務系統,也一定程度提高了接口文檔的安全性。核心的實現過程就是:

  • 在swagger2接口文檔所在的應用內,利用swagger2markup將接口文檔導出為adoc文件,也可以導出markdown文件。
  • 然後將adoc文件轉換為靜態的html格式,可以將html發布到nginx或者其他的web應用容器,提供訪問(本文不會講html靜態部署,只講HTML導出)。

注意:adoc是一種文件格式,不是我的筆誤。不是doc文件也不是docx文件。

一、maven依賴類庫

在已經集成了swagger2的應用內,通過maven坐標引入相關依賴類庫,pom.xml代碼如下:

<dependency>
    <groupId>io.github.swagger2markup</groupId>
    <artifactId>swagger2markup</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-core</artifactId>
    <version>1.5.16</version>
</dependency>
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-models</artifactId>
    <version>1.5.16</version>
</dependency>

swagger2markup用於將swagger2在線接口文檔導出為html,markdown,adoc等格式文檔,用於靜態部署或離線閱讀。其中第一個maven坐標是必須的。后兩個maven坐標,當你在執行後面的代碼過程中報下圖中的ERROR,或者有的類無法import的時候使用。

產生異常的原因已經有人在github的issues上給出解釋了:當你使用swagger-core版本大於等於1.5.11,並且swagger-models版本小於1.5.11就會有異常發生。所以我們顯式的引入這兩個jar,替換掉swagger2默認引入的這兩個jar。

二、生成adoc格式文件

下面的代碼是通過編碼方式實現的生成adoc格式文件的方式

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class DemoApplicationTests {
    @Test
    public void generateAsciiDocs() throws Exception {
        //    輸出Ascii格式
        Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
                .withMarkupLanguage(MarkupLanguage.ASCIIDOC) //設置生成格式
                .withOutputLanguage(Language.ZH)  //設置語言中文還是其他語言
                .withPathsGroupedBy(GroupBy.TAGS)
                .withGeneratedExamples()
                .withoutInlineSchema()
                .build();

        Swagger2MarkupConverter.from(new URL("http://localhost:8888/v2/api-docs"))
                .withConfig(config)
                .build()
                .toFile(Paths.get("src/main/resources/docs/asciidoc"));
    }
}
  • 使用RunWith註解和SpringBootTest註解,啟動應用服務容器。 SpringBootTest.WebEnvironment.DEFINED_PORT表示使用application.yml定義的端口,而不是隨機使用一個端口進行測試,這很重要。
  • Swagger2MarkupConfig 是輸出文件的配置,如文件的格式和文件中的自然語言等
  • Swagger2MarkupConverter的from表示哪一個HTTP服務作為資源導出的源頭(JSON格式),可以自己訪問試一下這個鏈接。8888是我的服務端口,需要根據你自己的應用配置修改。
  • toFile表示將導出文件存放的位置,不用加後綴名。也可以使用toFolder表示文件導出存放的路徑。二者區別在於使用toFolder導出為文件目錄下按標籤TAGS分類的多個文件,使用toFile是導出一個文件(toFolder多個文件的合集)。
@Test
public void generateMarkdownDocsToFile() throws Exception {
    //    輸出Markdown到單文件
    Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
            .withMarkupLanguage(MarkupLanguage.MARKDOWN)
            .withOutputLanguage(Language.ZH)
            .withPathsGroupedBy(GroupBy.TAGS)
            .withGeneratedExamples()
            .withoutInlineSchema()
            .build();

    Swagger2MarkupConverter.from(new URL("http://localhost:8888/v2/api-docs"))
            .withConfig(config)
            .build()
            .toFile(Paths.get("src/main/resources/docs/markdown"));
}

上面的這一段代碼是生成markdown格式接口文件的代碼。執行上面的2段單元測試代碼,就可以生產對應格式的接口文件。

還有一種方式是通過maven插件的方式,生成adoc和markdown格式的接口文件。筆者不常使用這種方式,沒有使用代碼生成的方式配置靈活,很多配置都放到pom.xml感覺很臃腫。但還是介紹一下,首先配置maven插件swagger2markup-maven-plugin。

<plugin>
    <groupId>io.github.swagger2markup</groupId>
    <artifactId>swagger2markup-maven-plugin</artifactId>
    <version>1.3.1</version>
    <configuration>
        <swaggerInput>http://localhost:8888/v2/api-docs</swaggerInput><!---swagger-api-json路徑-->
        <outputDir>src/main/resources/docs/asciidoc</outputDir><!---生成路徑-->
        <config>
            <swagger2markup.markupLanguage>ASCIIDOC</swagger2markup.markupLanguage><!--生成格式-->
        </config>
    </configuration>
</plugin>

然後運行插件就可以了,如下圖:

三、通過maven插件生成HTML文檔

<plugin>
    <groupId>org.asciidoctor</groupId>
    <artifactId>asciidoctor-maven-plugin</artifactId>
    <version>1.5.6</version>
    <configuration>
         <!--asciidoc文件目錄-->
        <sourceDirectory>src/main/resources/docs</sourceDirectory>
        <!---生成html的路徑-->
        <outputDirectory>src/main/resources/html</outputDirectory>
        <backend>html</backend>
        <sourceHighlighter>coderay</sourceHighlighter>
        <attributes>
            <!--導航欄在左-->
            <toc>left</toc>
            <!--显示層級數-->
            <!--<toclevels>3</toclevels>-->
            <!--自動打数字序號-->
            <sectnums>true</sectnums>
        </attributes>
    </configuration>
</plugin>

adoc的sourceDirectory路徑必須和第三小節中生成的adoc文件路徑一致。然後按照下圖方式運行插件。

HTMl接口文檔显示的效果如下,有了HTML接口文檔你想轉成其他各種格式的文檔就太方便了,有很多工具可以使用。這裏就不一一介紹了。

期待您的關注

  • 向您推薦博主的系列文檔:
  • 本文轉載註明出處(必須帶連接,不能只轉文字):。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

大型科技團隊的管理

介紹了高效科技組織的特點及管理經驗,指出科技團隊的定位和使命在於支持業務、賦能業務、最終引領業務,同時,還介紹了面向未來的科技組織的特點及對管理者提出的能力要求。

內容來源 | LeaTech全球CTO領導力峰會宜信公司CTO 高級副總裁向江旭分享《大型科技團隊的管理》

主講人 | 宜信公司CTO 高級副總裁向江旭

實錄整理 | 宜信技術學院成芳

引言:11月16日,由51CTO旗下CTO訓練營品牌精心打造的LeaTech全球CTO領導力峰會在北京粵財JW萬豪酒店拉開序幕。作為CTO、技術VP、技術總監等技術管理者人群的高端社交圈,本屆峰會現場聚集了CTO訓練營歷屆校友、CTO導師,以及行業中的資深技術管理者。600多位與會嘉賓在現場充分交流了有關技術性視野、技術領導力、技術團隊組織建設等精彩話題的觀點與思考,藉助峰會這個線下平台,技術管理者們积極探索了更多商業可能,開拓管理視野,令自身領導力再上新台階。

本次峰會邀請到宜信公司CTO 高級副總裁向江旭先生,帶來主題為《大型科技團隊的管理》的分享,向江旭先生在分享中提到科技團隊的定位和使命在於支持業務、賦能業務、最終引領業務,同時,他還介紹了面向未來的科技組織的特點及對管理者提出的能力要求。

以下為本次演講的分享實錄。

各位朋友下午好,今天我分享的主題是《大型科技團隊的管理》,非常高興能跟大家分享一些關於大型科技團隊管理的經驗和觀察。

去年12月,我在另一個峰會的分享中提到經濟寒冬、金融寒冬,今年這個時候也開始進入冬季,但是不管外面的大環境如何,技術人/技術圈都非常幸運,在科技賦能的市場中,技術人總是被需要的,技術圈也總是熱情高漲的。我認為,關於技術團隊的管理經驗非常值得與大家一起分享和交流。

一、大型科技團隊的特點及定位

大型科技團隊一般都有以下幾個特點:

  • 一定規模。顧名思義,談到大型科技團隊首先想到的特點肯定是團隊成員眾多。
  • 地域分佈廣。科技團隊的成員可能分佈在不同的地方。
  • 團隊背景多元。這種多元包括兩個層面的含義:一是角色背景多元,有的成員甚至不是做技術(這裏特指軟件研發)的,而是從其他行業轉過來的,比如行業數據分析師等角色。二是團隊成員的種族、國家背景多元。

一定規模、團隊背景多元化、分佈在不同地域等特點,使得大型科技團隊在管理上面臨着非常大的挑戰。

1.1 大型科技公司的科技團隊組織架構

上圖是一些大家耳熟能詳的漫畫,介紹了幾種典型的科技公司的科技團隊架構形式。

  • “Google式團隊架構”:最上面是兩個創始人和一個CEO,下面是部門負責人,其特點是下一級有多個彙報對象,這是谷歌的內部架構和管理方式,從一個層面代表了某個歷史階段科技組織的團隊構造。
  • “Amazon式團隊架構”:典型的層級式管理,逐級彙報。
  • “Oracle式團隊架構”:特點是有一個專門的法務團隊,因為Oracle收購了很多公司,某種程度上它是一個法務或銷售導向的公司。
  • “Facebook式團隊架構”:Facebook的團隊結構和它的業務一樣,是社交網絡形式的網狀結構。
  • “Apple式團隊架構”:(這是一張比較早期的圖)其特點是最核心的靈魂人物的觸角深入到公司的方方面面,事無巨細都是他在親自主導。
  • “Microsoft式團隊結構”:最後這張也是之前的圖,代表的是我服務過的公司-微軟。三大版塊部門沒有交集,很深的部門牆,互相之間存在比較激烈的內部鬥爭。

由此可見,大型科技公司的文化基因決定了其科技團隊的組織架構形式,而科技組織架構的設計和管理很大程度上決定了組織的效能。

1.2 科技團隊的使命和定位

在討論科技團隊的管理之前,有一個很重要的前提,要知道科技團隊的使命和定位是什麼。

很多非科技驅動的公司,比如銀行、保險公司、地產公司等,都在計劃或嘗試轉型成為一家科技公司。我認為在這樣的背景下,科技從業人員反而應該更清楚自己的定位。

1)支持業務

首先我們是支持業務的,要把業務放在最核心的位置,如果公司是靠產品和服務生存的,業務沒做好,科技也不一定能做好。當然公司類型不一樣,不能一概而論。比如純科技公司微軟,產品和服務本身就是技術,技術的好壞決定了公司業務的好壞。

2)賦能業務

科技團隊通過開發一些工具,幫助業務、銷售等部門實現業務流程信息化、智能化,使得業務流轉的過程更為暢通和友好。甚至更進一步,開發一個好的技術平台,使得生態圈、合作夥伴、第三方公司能夠在平台上發展業務、共建生態,這時科技團隊就起到了賦能業務的作用。

3)引領業務

科技團隊還可以通過技術創新、產品創新來拓展新的業務領域,催生新的業務模式,增加新的收入來源,成為引領業務的力量。

這三點是相輔相成的,支持業務、賦能業務,最後引領業務,不論是在技術驅動的公司,還是在業務驅動的公司,技術團隊的使命和定位都是如此。

1.3 技術和業務的關係

定位決定地位,業務發展是終極目標,當面臨整體技術戰略與商業戰略衝突、技術實施節點選擇、技術與業務路徑匹配等問題時,技術管理者可以從以下方面進行思考。

1)解決技術戰略與商業戰略的衝突。

技術和業務除了相輔相成的關係外,還存在一定的衝突。其實技術和業務的衝突在大家平時的工作中經常見到,比如業務同事着急上線一個功能來做活動;而技術覺得要達到同樣的目的,我們可能需要好的設計和架構,而不是簡單做一個臨時補丁式的功能,這就是很常見的技術和業務的衝突。

雖然技術負責人要對未來中長期的戰略布局保持持續思考,但這取決於公司的大小、規模和階段。如果是初創公司,首要任務是生存,那麼業務需求是最高優先級,首先要考慮解決系統不穩定、安全或其他問題;但對於有一定體量的企業,公司業務已經發展到一定階段,技術團隊也有一定的規模,當存在業務需求和技術戰略的衝突時,在滿足最緊迫的業務需求的同時,將一定精力投入到基礎技術研發中去,必須要做中長期的項目預研,做一些底層、甚至風險較高的研究。

2)技術變革的實施節點

我們永遠在高速公路上奔跑,一邊行駛一邊換輪子或換部件的事情一直在發生。技術重構、技術變革、技術債務償還的時機和節點和對業務產生的影響,是我們面臨的又一個挑戰,也是需要我們長期考慮的問題。

什麼節點選擇什麼樣的技術?技術負責人具體把握新技術引入節點,其實難度很大。如果新技術距離實現商用價值僅有一年時間,那麼必須要進行布局,申請預算,建立團隊推進;如果新技術商業化已經迫在眉睫,競爭對手已經在布局了,那麼採取的措施就不是從頭做起,更好的應對方式可能是進行併購或資本運作。

3)技術與業務路徑匹配

新技術來臨,對業務的影響和衝擊會分短、中、長期,有的技術在短期內對新的業態有幫助,有的技術需要一個比較長的周期才會對業務產生影響。這時技術領導人要評估技術本身,並將其與業務的戰略路徑進行匹配,如果對長期業務有幫助,新技術仍然要引入,只是選擇時間點、投入範圍等可能會不一樣。

同時,在引入新技術進行技術創新時,還要注意新技術對當前業務產生的影響。舉個例子,宜信是一家金融科技公司,有自己的催收部門,我們一方面通過規範催收人員的行為來進行催收,一方面自己研發催收機器人。催收機器人的出現意味着部分催收人員的工作將會被替代,同時,機器人的特點之一是沒有情緒,它會按照程序設定禮貌地和用戶溝通,這就會在一定程度對催收效果和業務產生影響。因此我們還要考慮在哪個環節使用機器人這個技術,帶來的效果會更好。這也是技術和業務相輔相成又存在矛盾衝突關係的體現。

1.4 科技戰略

對於大型科技團隊而言,科技戰略思維也非常重要。舉幾個例子。

舉例提到的是我工作過的幾家公司,它們在不同的時間點做了一些不同的戰略調整。

很多年以前,在中國90%的Windows都不是正版的,於是微軟內部提出一個計劃,希望Windows在中國免費。這個計劃現在看起來非常簡單,也很容易理解,正版Windows免費帶來的好處顯而易見:它是一個非常好的終端用戶觸點、可以獲取更多用戶,可以基於這些用戶數據做數據分析和精準營銷等。但是當時微軟Windows的老大極力反對這件事,他認為這會影響Windows的營收,因此這個計劃最終沒有實行。這就是戰略思維的問題,如果不是在中國本地親身體驗這個環境和市場,可能做出的戰略決策就不一定是準確的,帶來的結果可能是痛失更大的發展機會。

微軟後來的雲戰略轉型是成功的,而移動轉型卻是失敗的。移動轉型時期,微軟收購諾基亞旗下的大部分手機業務,並基於Windows自研出一個Windows操作系統放在手機硬件上。因為微軟覺得沒有推出自己的手機這是一個缺憾,還認為佔領移動終端必須基於Windows,因此作出這樣的決策,結果以失敗告終。Windows使微軟一度成為桌面系統垄斷的贏家,也使得微軟在移動轉型失敗,成為下一步發展的絆腳石,可謂是成也Windows,敗也Windows。

再看蘇寧,蘇寧受到阿里巴巴、京東等電商的衝擊,痛下決心必須做電商,它採取的戰略是結合自己線下門店的優勢做O2O智慧門戶,實行線上下單、線下體驗、送貨到家,這種全渠道、全觸點的用戶交互與服務形式,使得蘇寧成為“中國傳統行業数字化轉型互聯網”為數不多的成功案例之一。

現在金融行業正處於嚴監管的環境,金融公司該何去何從?在這裏分享一些我的看法。

縱觀改革開放以來的歷史,每個行業在開始初期都是開放的,任何人都可以參与進來,魚龍混雜,一旦行業出現亂象,就會有監管介入,那些能力不強的、不合規的會被淘汰,等到監管后再開放、行業再成熟時,最後存活下來的才能健康發展。金融行業也是如此。

科技同仁不僅要埋頭做好自己的工作,也要抬頭看看歷史和未來,思考我們身處的這個行業,我們從事的科技工作對公司、對行業的作用是什麼?行業未來的發展趨勢是什麼,這對我們未來的職業發展也有幫助。

二、大型科技團隊的管理實踐

2.1 成功科技組織的特點

無論是前面提到的國際科技巨頭,還是國內優秀的互聯網公司,成功的科技組織都具備一些共同的特點。

1)高效、敏捷

優秀的科技團隊一定是一個高效、敏捷的團隊,能夠快速響應用戶和市場的需求變化,快速上線產品、得到反饋、不斷迭代更新,滿足或超過用戶的預期。

2)商業思維

科技團隊一定要有商業頭腦、商業思維,因為無論是搭建系統,還是做APP,一定有用戶,我們需要真正洞察到用戶的痛點和問題,幫他們及時解決問題,給他們創造價值。

3)數據驅動

如果科技團隊的工作只圍繞產品經理提出的需求,未來的產品路線圖還不夠,一定要基於用戶反饋、市場反饋、日活、月活、留存、轉化等數據來驅動產品走向、技術走向。

4)變革創新

很多公司都在強調變革和創新,我認為這是科技團隊必須要打造的環境和氛圍,方式很多,比如組織各種各樣的黑客馬拉松、團隊之間的交流分享等。我們公司也在組織黑客馬拉松,每個月業務部門都有比較棘手或緊迫的項目,技術團隊與業務團隊聯合把這些業務痛點解決,成果馬上應用到業務環境中去。

2.2 績效管理

績效管理的機制在微軟實行了很多年,其強制5%、10%的淘汰機制,一直被人詬病,因為它使得很多團隊互相指責、互相拆台,有的員工為了績效“寧當雞頭不當鳳尾”,組織內形成不好的文化和結果。後來改成了5%、10%的獎勵,從懲罰後進者變成鼓勵先進者,取得的效果好很多。

2.3 溝通

溝通每天都在進行,比如各種大大小小的會議。我發現不管開多少會,高層領導的想法、思路並不一定能被所有同事理解,因為溝通方式、溝通渠道的原因,信息不能觸達到所有人,需要重複很多遍。

我們可以利用一些非正式場合或社交媒體來進行交流。比如我們公司有每月的CTO午餐會,抽籤的方式,各團隊都有機會可以坐下來跟我一起吃午餐,通過這種面對面的交流,我會向團隊成員提出一些我的看法,或者推薦一本書,他們會向我反饋當時的痛點、想法等。我覺得這是一種非常好的機制,大家能夠在一個寬鬆的環境下面對面地交流。記得以前在思科工作的時候,也有CEO早餐會,每個月過生日的員工可以跟CEO吃早餐,也是一種很好的溝通互動的方式。

2.4 團隊文化

團隊文化包含很多要素,我對這幾點的認同感比較深:主人翁意識、緊迫感、同理心。

2.5 技術決策

商業世界充滿了選擇和決策。作為一個技術決策人,有時很難做決定,難做決定就意味着拖延,這一點對於大型科技團隊的決策人來說是比較忌諱的,很多時候不在於你做的決策是對還是錯,而是在於你做不做決定。如果你是一個不敢做決定的人,帶領的團隊就會缺乏方向感,你做的決定就會受到團隊的質疑和挑戰。

有時候即使你做出了錯誤的決定,但大家一起努力執行的時候,可能可以逐步調整轉變從而達成正確的結果。技術管理人一定要懂得取捨,通過大腦計算,明確目標,了解關聯方,分析可選項和利弊,基於目標、數據和對未來的預期做出決策。

2.6 執行力

執行的時候要不忘初心,一步步地大處着眼、小處着手,小步快跑,快速迭代,及時反饋,及時調整,朝着設定的目標專心致志、心無旁騖。

2.7 終極目標

最後的終極目標是,希望科技團隊能夠做到比業務更懂業務,比用戶更懂用戶,讓技術本身變成公司核心的業務。我認為如果能做到科技團隊在公司起到了核心的作用,這才是永生的價值。

三、面向未來的科技組織

3.1 回顧微軟的3任CEO和3種組織

就微軟而言,同一家公司,文化和技術氛圍在不同階段是不一樣的。

第一個階段,CEO比爾蓋茨,技術為王,認為技術改變世界,代碼可以改變千萬人的生活。這一階段造就了好的產品,同時也存在一些負面影響,比如垄斷等。

第二個階段,CEO鮑爾默,業績為王,要求所有設備,包括手機、電視、車等,都跑到Windows上,還和海爾合作推出基於Windows的智能電視,當時所有跟Windows衝突的想法和計劃基本都被扼殺在搖籃里,這也導致公司錯失了很多發展的機會。

第三個階段,CEO納德拉,公司文化變得更加包容、強調同理心、開放透明,包括技術開源、提供雲服務、和蘋果/谷歌等競爭對手合作。這種開放包容的氛圍,讓微軟得以浴火重生。

3.2 面向未來的科技組織

面向未來的科技組織應該是什麼樣的?

1)跨界融合

不同背景的人,融合到一個跨界的氛圍和組織中,一起發力。

2)数字化轉型

傳統公司也在做数字化轉型,這時會引入很多科技人才,利用科技幫助公司完成轉型,並在行業實現快速發展。

3)全球共享、全球分佈

團隊組成全球化,來自不同國家、不同種族的人組成一個全球化的團隊。東南亞有一個集美團、滴滴、螞蟻金服為一體的公司,解決出行、支付、快遞的問題,這家公司有一個幾千人的科技團隊,團隊成員來自50多個種族,分佈在美國、新加坡、北京、印尼、印度等國家和地區。

4)技術引領

未來科技團隊不僅要做到賦能業務,還要能實現引領業務。

這樣的科技團隊需要跨界、全球化、複合型的領導人,只有既懂科技、又懂管理;既要懂業務,又懂行業的管理人才才能適合於領導面向未來的科技組織。

以上就是今天跟大家分享的全部內容,時間很短,主要介紹了一些在不同公司不同行業的大型科技團隊的管理經驗,希望大家在面向未來的科技組織中找到自己的定位,成為一個優秀的領導者。謝謝大家。

本文根據向江旭老師在LeaTech全球CTO領導力峰會上的分享內容整理所得,轉載請聯繫授權。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

3c收購,鏡頭 收購有可能以全新價回收嗎?

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

賣IPHONE,iPhone回收,舊換新!教你怎麼賣才划算?

文件與文件系統壓縮

目錄

在Linux下面有相當多的壓縮命令可以運行,這些壓縮命令可以讓我們更方便地從網絡上面下載容量較大的文件。此外,我們知道在Linux下面,擴展名沒有什麼特殊的意義。 不過,針對這些壓縮命令所產生的壓縮文件,為了方便記憶,還是會有一些特殊的命名方式,就讓我們來看看吧!

文件壓縮

什麼是文件壓縮呢?我們稍微談一談它的原理,目前我們使用的計算機系統中都是使用所謂的字節單位來計量。不過,事實上,計算機最小的計量單位應該是bit才對,此外,我們也知道 1字節=8比特(1Byte=8bit),但是如果今天我們只是記錄一個数字,即1這個数字,它會如何記錄?假設一個字節可以看成下面的模樣:

由於 1Byte=8bit,所以每個字節當中會有8個空格,而每個空格只可以是0、1

由於我們記錄的数字是1,考慮計算機所謂的二進制,如此一來,1會在最右邊佔據1個位,而其他的7個位將會自動地被填上0.如下圖所示

你看看,其實在這樣的例子中,那7個位應該是空的才對。不過,為了要滿足目前我們的操作系統數據的讀寫,所以就會將該數據轉為字節的形式來記錄。而一些聰明的計算機工程師就利用一些複雜的計算方式,將這些沒有使用到的空間【丟】出來,以讓文件佔用的空間變小,這就是壓縮的技術。
另一種壓縮技術也很有趣,它是將重複的數據進行統計記錄。舉例來說,如果你的數據為【111······】共有100個1時,那麼壓縮技術會記錄為【100個1】而不是真的有100個1的位存在。這樣也能夠精簡文件記錄的容量,非常有趣吧!
簡單地說,你可以將它想成,其實文件裏面有相當多的空間存在,並不是完全填滿的,而壓縮技術就是將這些空間填滿,以讓整個文件佔用的容量下降。不過,這些壓縮過的文件並無法直接被我們的操作系統所使用,因此,若要使用這些被壓縮過的文件數據,則必須將它還原回未壓縮前的模樣,那就是所謂的解壓縮。而至於壓縮后與壓縮的文件所佔用的磁盤空間大小,就可以被稱為是壓縮比
這個壓縮與解壓縮的操作有什麼好處呢?
1.最大的好處就是壓縮過的文件容量變小了,所以你的硬盤無形之中就可以容納更多的數據。
2.此外,在一些網絡數據的傳輸中,也會由於數據量的降低,好讓網絡帶寬可以用來做更多的工作,而不是老卡在一些大型文件傳輸上面。

Linux系統常見壓縮命令

在Linux的環境中,壓縮文件的擴展名大多是: *.tar、*.tar.gz、*.gz、*.Z、*.bz2、*.xz。為什麼會有這樣的擴展名?不是說Linux的擴展名沒有什麼作用嗎?
這是因為Linux支持的壓縮命令非常多,且不同的命令所用的壓縮技術並不相同,當然彼此之間可能就無法互通/解壓縮文件。所以,當你下載到某個文件時,自然就需要知道該文件是由哪種壓縮命令所製作出來的,好用來對照對照着解壓縮,也就是說,雖然Linux文件的屬性基本上是與文件名沒有絕對關係的,但是為了幫助我們人類小小的腦袋,所以適當的擴展名還是必要的,下面我們就列出幾個常見的壓縮文件擴展名:

*.gz         gzip程序壓縮的文件
*.bz2        bzip2程序壓縮的文件
*.xz         xz程序壓縮的文件
*.zip        zip程序壓縮的文件
*.Z          compress程序壓縮的文件
*.tar        tar程序打包的文件,並沒有壓縮過
*.tar.gz     tar程序打包的文件,並且經過gzip的壓縮
*.tar.bz2    tar程序打包的文件,並且經過bzip2的壓縮
*.tar.xz     tar程序打包的文件,並且經過xz的壓縮

Linux常見的壓縮命令就是gzip、bzip2以及最新的xz,至於compress已經不流行了。為了支持windows常見的zip,其實Linux也早就有zip命令了。gzip是由GNU計劃所開發出來的壓縮命令,該命令支持已經替換了compress。後台GNU又開發出了bzip2及xz這幾個壓縮比更好的壓縮命令。不過,這些命令通常僅能針對一個文件來壓縮與解壓縮,如此一來,每次壓縮與解壓縮都要一大堆文件,豈不煩人?此時,這個所謂的【打包軟件,tar】就顯得很重要。
這個tar可以將很多文件打包成一個文件,甚至是目錄也可以這麼玩。不過,單純的tar功能僅僅是打包而已,即將很多文件結合為一個文件,事實上,它並沒有提供壓縮的功能,後台,GNU計劃中,將整個tar與壓縮的功能結合在一起,如此一來,提供用戶更方便且更強大的壓縮與打包功能,下面我們就來談一談這些在Linux下面基本的壓縮命令。

gzip

gzip可以說是應用最廣的壓縮命令了,目前gzip可以解開compress、zip和gzip等軟件所壓縮的文件,至於gzip所建立的壓縮文件為*.gz,讓我們來看看這個命令的語法:

gzip [-cdtvn] 文件名
選項與參數:
-c: 將壓縮的數據輸出到屏幕上,可通過數據流重定向來處理;
-d: 解壓縮的參數;
-t: 可以用來檢驗一個壓縮文件的一致性,看看文件有無錯誤;
-v: 可以显示出原文件/壓縮文件的壓縮比等信息;
-n: n為数字的意思,代表壓縮等級,-1最快,但壓縮比最差,-9最慢,但是壓縮比最好,默認是-6

示例1:壓縮文件(gzip -v 文件名)

示例2:解壓縮文件(gzip -d 文件名)

示例3:按照指定壓縮比壓縮(gzip -9 文件名)

示例4:查看壓縮文件的內容(zcat 文件名)

示例5:壓縮為指定文件名(gzip -c 文件名 > 指定文件名)

當你使用gzip進行壓縮時,在默認的狀態下原本的文件會被壓縮成為.gz後綴的文件,源文件就不存在了,這點與一般習慣使用Windows做壓縮的朋友所熟悉的情況不同,要注意。cat/more/less可以使用不同的方式來讀取純文本文件,那麼zcat/zmore/zless則可以對應於cat/more/less的方式來讀取純文件文件被壓縮后的壓縮文件。

bzip2

若說gzip是為了替換compress並提供更好的壓縮比而成立的,那麼bzip2則是為了替換gzip並提供更加的壓縮比而來。bzip2真是很不錯的東西,這玩意的壓縮比竟然比gzip還要好,至於bzip2的用法幾乎與gzip相同,看看下面的用法吧!

bzip2 [-cdkzvn] 文件名
選項與參數:
-c: 將壓縮的數據輸出到屏幕上,可通過數據流重定向來處理;
-d: 解壓縮的參數;
-k: 保留原始文件,而不是刪除原始文件;
-z: 壓縮的參數(默認值,可以不加);
-v: 可以显示出原文件/壓縮文件的壓縮比等信息;
-n: n為数字的意思,代表壓縮等級,-1最快,但壓縮比最差,-9最慢,但是壓縮比最好,默認是-6

示例:

bzip2 -v 待壓縮文件名
bzip2 -d 壓縮后的文件名
bzip2 -9 -c 待壓縮的文件名 > 自定義壓縮文件名

xz

雖然bzip2已經具有很棒的壓縮比,不過顯然某些自由軟件開發者還不滿足,因此後來還推出了xz這個壓縮比更高的軟件。這個軟件的用法也跟gzip/bzip2幾乎一模一樣,那我們就來看一看。

xz [-cdtlkn] 文件名
選項與參數:
-c: 將壓縮的數據輸出到屏幕上,可通過數據流重定向來處理;
-d: 解壓縮的參數;
-k: 保留原始文件,而不是刪除原始文件;
-l: 列出壓縮文件的相關信息;
-t: 測試壓縮文件的完整性,看看有沒有錯誤;
-z: 壓縮的參數(默認值,可以不加);
-n: n為数字的意思,代表壓縮等級,-1最快,但壓縮比最差,-9最慢,但是壓縮比最好,默認是-6

示例:

xz -v 待壓縮的文件名
xz -l 壓縮后的文件名
xz -d 壓縮后的文件名
xz -k 待壓縮的文件名

打包命令

前面談到的命令大多僅能針對單一文件來進行壓縮,雖然gzip、bzip2、xz也能夠針對目錄來進行壓縮,不過,這幾個命令對目錄的壓縮指的是將目錄內的所有文件【分別】進行壓縮的操作。而不像在Windows的系統,可以使用類似WinRAR這一類的壓縮軟件來將好多數據包成一個文件的樣式。
這種將多個文件或目錄包成一個大文件的命令功能,我們可以稱它是一種打包命令,那Linux有沒有這種打包命令?有,那就是大名鼎鼎的tar,tar可以將多個目錄或文件打包成一個大文件,同時還可以通過gzip、bzip2、xz的支持,將該文件同時進行壓縮。更有趣的是,由於tar的使用太廣泛了,目前Windows的WinRAR也支持.tar.gz文件名的解壓縮。

tar

tar的選項與參數特別多,我們只講幾個常用的選項,更多選項您可以自行man tar查詢。

tar [-z|-j|-J] [cv] [-f 待建立的新文件名] filename... <== 打包與壓縮。
tar [-z|-j|-J] [cv] [-f 既有的tar文件名]  <== 查看文件名
tar [-z|-j|-J] [xv] [-f 既有的tar文件名]  <== 解壓縮
選項與參數:
-c: 建立打包文件,可搭配-v來查看過程中被打包的文件名(filename);
-t: 查看打包文件的內容含有那些文件名,重點在查看【文件名】;
-x: 解包或解壓縮功能,可以搭配-C(大寫)在特定目錄解壓,特別留意的是,-c、-t、-x不可同時出現在一串命令行中;
-z: 通過gzip的支持進行壓縮/解壓縮: 此時文件名最好為*.tar.gz;
-j: 通過bzip2的支持進行壓縮/解壓縮:此時文件名最好為*.tar.bz2;
-J: 通過xz的支持進行壓縮/解壓縮: 此時文件名最好為 *.tar.xz,特別留意,-z、-j、-J不可以同時出現在一串命令行中;
-v: 在壓縮/解壓縮的過程中,將正在處理的文件名显示出來;
-f filename: -f後面要立刻接要被處理的文件名,建議-f單獨寫一個選項(比較不會忘記)。
-C 目錄: 這個選項用在解壓縮,若要在特定目錄解壓縮,可以使用這個選項
-p(小寫): 保留備份數據的原本權限與屬性,常用於備份(-c)重要的配置文件;
-P(大寫): 保留絕對路徑,亦即允許備份數據中含有根目錄存在之意

其實最簡單的使用tar就只要記住下面的命令即可:

  • 壓縮: tar -jcv -f filename.tar.bz2 要被壓縮的文件或目錄名稱;
  • 查詢: tar -jtv -f filename.tar.bz2
  • 解壓縮: tar -jxv -f filename.tar.bz2 -C 欲解壓縮的目錄

示例:

tar -zcvf 文件名.tar.gz 文件名(目錄)

tar -ztvf 文件名.tar.gz

tar -zxvf 文件名.tar.gz

資料:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※公開收購3c價格,不怕被賤賣!

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

Redis是什麼?看這一篇就夠了

本文由葡萄城技術團隊編撰並首發

轉載請註明出處:,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。

引言

在Web應用發展的初期,那時關係型數據庫受到了較為廣泛的關注和應用,原因是因為那時候Web站點基本上訪問和併發不高、交互也較少。而在後來,隨着訪問量的提升,使用關係型數據庫的Web站點多多少少都開始在性能上出現了一些瓶頸,而瓶頸的源頭一般是在磁盤的I/O上。而隨着互聯網技術的進一步發展,各種類型的應用層出不窮,這導致在當今雲計算、大數據盛行的時代,對性能有了更多的需求,主要體現在以下四個方面:

  1. 低延遲的讀寫速度:應用快速地反應能極大地提升用戶的滿意度
  2. 支撐海量的數據和流量:對於搜索這樣大型應用而言,需要利用PB級別的數據和能應對百萬級的流量
  3. 大規模集群的管理:系統管理員希望分佈式應用能更簡單的部署和管理
  4. 龐大運營成本的考量:IT部門希望在硬件成本、軟件成本和人力成本能夠有大幅度地降低

為了克服這一問題,NoSQL應運而生,它同時具備了高性能、可擴展性強、高可用等優點,受到廣泛開發人員和倉庫管理人員的青睞。

Redis是什麼

Redis是現在最受歡迎的NoSQL數據庫之一,Redis是一個使用ANSI C編寫的開源、包含多種數據結構、支持網絡、基於內存、可選持久性的鍵值對存儲數據庫,其具備如下特性:

  • 基於內存運行,性能高效
  • 支持分佈式,理論上可以無限擴展
  • key-value存儲系統
  • 開源的使用ANSI C語言編寫、遵守BSD協議、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的API

相比於其他數據庫類型,Redis具備的特點是:

  • C/S通訊模型
  • 單進程單線程模型
  • 豐富的數據類型
  • 操作具有原子性
  • 持久化
  • 高併發讀寫
  • 支持lua腳本

哪些大廠在使用Redis?

  • github
  • twitter
  • 微博
  • Stack Overflow
  • 阿里巴巴
  • 百度
  • 美團
  • 搜狐

Redis的應用場景有哪些?

Redis 的應用場景包括:緩存系統(“熱點”數據:高頻讀、低頻寫)、計數器、消息隊列系統、排行榜、社交網絡和實時系統。

 

Redis的數據類型及主要特性

Redis提供的數據類型主要分為5種自有類型和一種自定義類型,這5種自有類型包括:String類型、哈希類型、列表類型、集合類型和順序集合類型。

String類型:

它是一個二進制安全的字符串,意味着它不僅能夠存儲字符串、還能存儲圖片、視頻等多種類型, 最大長度支持512M。

對每種數據類型,Redis都提供了豐富的操作命令,如:

  • GET/MGET
  • SET/SETEX/MSET/MSETNX
  • INCR/DECR
  • GETSET
  • DEL

哈希類型:

該類型是由field和關聯的value組成的map。其中,field和value都是字符串類型的。

Hash的操作命令如下:

  • HGET/HMGET/HGETALL
  • HSET/HMSET/HSETNX
  • HEXISTS/HLEN
  • HKEYS/HDEL
  • HVALS

列表類型:

該類型是一個插入順序排序的字符串元素集合, 基於雙鏈表實現。

List的操作命令如下:

  • LPUSH/LPUSHX/LPOP/RPUSH/RPUSHX/RPOP/LINSERT/LSET
  • LINDEX/LRANGE
  • LLEN/LTRIM

集合類型:

Set類型是一種無順序集合, 它和List類型最大的區別是:集合中的元素沒有順序, 且元素是唯一的。

Set類型的底層是通過哈希表實現的,其操作命令為:

  • SADD/SPOP/SMOVE/SCARD
  • SINTER/SDIFF/SDIFFSTORE/SUNION

Set類型主要應用於:在某些場景,如社交場景中,通過交集、並集和差集運算,通過Set類型可以非常方便地查找共同好友、共同關注和共同偏好等社交關係。

順序集合類型:

ZSet是一種有序集合類型,每個元素都會關聯一個double類型的分數權值,通過這個權值來為集合中的成員進行從小到大的排序。與Set類型一樣,其底層也是通過哈希表實現的。

ZSet命令:

  • ZADD/ZPOP/ZMOVE/ZCARD/ZCOUNT
  • ZINTER/ZDIFF/ZDIFFSTORE/ZUNION

Redis的數據結構

Redis的數據結構如下圖所示:

關於上表中的部分釋義:

  1. 壓縮列表是列表鍵和哈希鍵的底層實現之一。當一個列表鍵只包含少量列表項,並且每個列表項要麼就是小整數,要麼就是長度比較短的字符串,Redis就會使用壓縮列表來做列表鍵的底層實現
  2. 整數集合是集合鍵的底層實現之一,當一個集合只包含整數值元素,並且這個集合的元素數量不多時,Redis就會使用整數集合作為集合鍵的底層實現

如下是定義一個Struct數據結構的例子:

 

簡單動態字符串SDS (Simple Dynamic String)

基於C語言中傳統字符串的缺陷,Redis自己構建了一種名為簡單動態字符串的抽象類型,簡稱SDS,其結構如下:

SDS幾乎貫穿了Redis的所有數據結構,應用十分廣泛。

SDS的特點

和C字符串相比,SDS的特點如下:

  1. 常數複雜度獲取字符串長度

    Redis中利用SDS字符串的len屬性可以直接獲取到所保存的字符串的長
    度,直接將獲取字符串長度所需的複雜度從C字符串的O(N)降低到了O(1)。

  2. 減少修改字符串時導致的內存重新分配次數

    通過C字符串的特性,我們知道對於一個包含了N個字符的C字符串來說,其底層實現總是N+1個字符長的數組(額外一個空字符結尾)

    那麼如果這個時候需要對字符串進行修改,程序就需要提前對這個C字符串數組進行一次內存重分配(可能是擴展或者釋放) 

    而內存重分配就意味着是一個耗時的操作。

Redis巧妙的使用了SDS避免了C字符串的缺陷。在SDS中,buf數組的長度不一定就是字符串的字符數量加一,buf數組裡面可以包含未使用的字節,而這些未使用的字節由free屬性記錄。

與此同時,SDS採用了空間預分配的策略,避免C字符串每一次修改時都需要進行內存重分配的耗時操作,將內存重分配從原來的每修改N次就分配N次——>降低到了修改N次最多分配N次。

如下是Redis對SDS的簡單定義:

  

Redis特性1:事務

  • 命令序列化,按順序執行
  • 原子性
  • 三階段: 開始事務 – 命令入隊 – 執行事務
  • 命令:MULTI/EXEC/DISCARD

Redis特性2:發布訂閱(Pub/Sub)

  • Pub/sub是一種消息通訊模式
  • Pub發送消息, Sub接受消息
  • Redis客戶端可以訂閱任意數量的頻道
  • “fire and forgot”, 發送即遺忘
  • 命令:Publish/Subscribe/Psubscribe/UnSub

  

Redis特性3:Stream

  • Redis 5.0新增
  • 等待消費
  • 消費組(組內競爭)
  • 消費歷史數據
  • FIFO

 

 

 

以上就是Redis的基本概念,下面我們將介紹在開發過程中可能會踩到的“坑”。

Redis常見問題解析:擊穿

概念:在Redis獲取某一key時, 由於key不存在, 而必須向DB發起一次請求的行為, 稱為“Redis擊穿”。

引發擊穿的原因:

  • 第一次訪問
  • 惡意訪問不存在的key
  • Key過期

合理的規避方案:

  • 服務器啟動時, 提前寫入
  • 規範key的命名, 通過中間件攔截
  • 對某些高頻訪問的Key,設置合理的TTL或永不過期

Redis常見問題解析:雪崩

概念:Redis緩存層由於某種原因宕機后,所有的請求會湧向存儲層,短時間內的高併發請求可能會導致存儲層掛機,稱之為“Redis雪崩”。

合理的規避方案:

  • 使用Redis集群
  • 限流

Redis在產品開發中的應用實踐

為此,我很高興的為大家介紹,葡萄城架構師Jim將在2019-11-27 14:00 為大家帶來一場公開課,其中 Jim除了為大家講解Redis的基礎,同時也會實際演示他所在的項目組使用Redis時碰到的問題以及解決方案,對於剛接觸Redis的同學來說,更具參考意義和學習價值,歡迎大家屆時參加,公開課地址:。

  • 後端採用nodeJS
  • 使用Azure的Redis服務
  • Redis的使用場景

    -  token緩存, 用於令牌驗證

    -  IP白名單

碰到的問題

  • “網絡抖動”或者Redis服務異常導致Redis訪問超時
  • Redis客戶端驅動穩定性問題

    -  連接池 “Broken connection” 問題

    -  JS的Promise引出的Redis重置問題

下面我們來簡單了解一下Redis的進階知識。

進階之Redis協議簡介

Redis客戶端通訊協議:RESP(Redis Serialization Protocol),其特點是:

  • 簡單
  • 解析速度快
  • 可讀性好

Redis集群內部通訊協議:RECP(Redis Cluster Protocol ) ,其特點是:

  • 每一個node兩個tcp 連接
  • 一個負責client-server通訊(P: 6379)
  • 一個負責node之間通訊(P: 10000 + 6379)

 

Redis協議支持的數據類型:

  • 簡單字符(首字節: “+”)

        “+OK\r\n”

  • 錯誤(首字節: “-”)

        “-error msg\r\n”

  • 数字(首字節: “:”)

        “:123\r\n”

  • 批量字符(首字節: “$”)

        “&hello\r\nWhoa re you\r\n”

  • 數組(首字節: “*”)

        “*0\r\n”

        “*-1\r\n”

除了Redis,還有什麼NoSQL型數據庫

市面上類似於Redis,同樣是NoSQL型的數據庫有很多,如下圖所示,除了Redis,還有MemCache、Cassadra和Mongo。下面,我們就分別對這幾個數據庫做一下簡要的介紹:

 

 

Memcache這是一個和Redis非常相似的數據庫,但是它的數據類型沒有Redis豐富。Memcache由LiveJournal的Brad Fitzpatrick開發,作為一套分佈式的高速緩存系統,被許多網站使用以提升網站的訪問速度,對於一些大型的、需要頻繁訪問數據庫的網站訪問速度的提升效果十分顯著。

Apache Cassandra(社區內一般簡稱為C*)這是一套開源分佈式NoSQL數據庫系統。它最初由Facebook開發,用於儲存收件箱等簡單格式數據,集Google BigTable的數據模型與Amazon Dynamo的完全分佈式架構於一身。Facebook於2008將 Cassandra 開源,由於其良好的可擴展性和性能,被 Apple、Comcast、Instagram、Spotify、eBay、Rackspace、Netflix等知名網站所採用,成為了一種流行的分佈式結構化數據存儲方案。

MongoDB:是一個基於分佈式文件存儲、面向文檔的NoSQL數據庫,由C++編寫,旨在為WEB應用提供可擴展的高性能數據存儲解決方案。MongoDB是一個介於關係數據庫和非關係數據庫之間的產品,是非關係數據庫當中功能最豐富,最像關係型數據庫的,它支持的數據結構非常鬆散,是一種類似json的BSON格式。

總結

以上就是Redis入門介紹教程,如果各位還想了解更多,歡迎通過評論和私信的方式告訴我。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

3c收購,鏡頭 收購有可能以全新價回收嗎?

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

賣IPHONE,iPhone回收,舊換新!教你怎麼賣才划算?

原創|我是如何從零學習開發一款跨平台桌面軟件的(Markdown編輯器)

原始衝動

最近一直在學習 Electron 開發桌面應用程序,目的是想做一個桌面編輯器,雖然一直在使用Typora這款神器,但無奈Typora太過國際化,在國內水土不服,無法滿足我的一些需求。

比如實現本地圖片上傳到雲端(mac版可以藉助iPic),無法幫我把本地圖片和文章一起發布到博客園、CSDN、SegmentFault、掘金等國內知名博客平台,要麼使用一些免費或付費的圖床,藉助類似iPic的工具,把圖片一鍵上傳到雲端。

我個人也嘗試過七牛雲的免費10G存儲空間,但是說實話,這些免費的空間到最後一定是為了讓你成為付費用戶,各種限制各種吐槽在網上很容易可以搜索到。

免費的圖床如新浪微博等,還算是比較好的圖床工具,相比一些網絡上的壓根不知道啥公司甚至是歸屬個人的免費圖床,新浪應該是比較靠譜的,相對來說可以保證圖片的存活時間,我個人用過一些免費的圖床網站,記得印象深刻的就是服務器出問題,網站掛個公告,曾經的圖片再去訪問就是默認的404。

雖然新浪家大業大不是說倒閉就倒閉的,圖片相對穩定可靠,不過新浪的圖片服務器會檢測訪問來源Referer來防止外部網站引用,造成訪問403。

總結起來就是一句話,圖片還是隨着文章一鍵發布到博客平台比較好。要丟一起丟~

心理掙扎

緣起這個動機,但是下定決心依舊是困難重重。

我個人是一個Java工程師,雖說搞過Andorid、HTML前端,但對前端深感不適的我果斷放棄了。對於桌面程序開發,我連Swing都不會,造一個Markdown編輯器有點難,何況還要加上這些定製功能。

猶猶豫豫,還是決定去嘗試一下。於是調研寫跨平台的一些途徑。

先嘗試Swing,不過Swing不好實現我期望的一些功能,改成JavaFX倒是可以,不過說實話,寫起來很累,太過繁瑣,就放棄了。最後把目光瞄向electron,就它了,HTML+Js+Css,聽起來就很簡單,事實證明,無論是測試還是打包都很方便。

決定之後,便開始進行 Electron 的系統學習。

邁出第一步

第一步就是安裝 Electron 的本地開發環境,這也是大多數應用開發的第一步。

你需要安裝 Node.js 在你的本地電腦,Electron 也是依賴於 Node.js 的環境,嚴格來說, Electron 通過將 Chromium 和 Node.js 合併到同一個運行時環境中,並將其打包為Mac,Windows和Linux系統下的應用來實現這一目的。

關於 Electron 的具體開發流程,這裏不再贅述,你完全可以在開發中使用Web前端開發的思維,除了在處理多個窗口之間交互的時候,就不得不了解Eelctron的進程機制。

主進程和渲染進程

Electron 運行 package.json 的 main 腳本的進程被稱為主進程。 在主進程中運行的腳本通過創建web頁面來展示用戶界面。 一個 Electron 應用總是有且只有一個主進程。

由於 Electron 使用了 Chromium 來展示 web 頁面,所以 Chromium 的多進程架構也被使用到。 每個 Electron 中的 web 頁面運行在它自己的渲染進程中。

在普通的瀏覽器中,web頁面通常在沙盒環境中運行,並且無法訪問操作系統的原生資源。 然而 Electron 的用戶在 Node.js 的 API 支持下可以在頁面中和操作系統進行一些底層交互。

主進程與渲染進程的區別

主進程使用 BrowserWindow 實例創建頁面。 每個 BrowserWindow 實例都在自己的渲染進程里運行頁面。 當一個 BrowserWindow 實例被銷毀后,相應的渲染進程也會被終止。

主進程管理所有的web頁面和它們對應的渲染進程。 每個渲染進程都是獨立的,它只關心它所運行的 web 頁面。

在頁面中調用與 GUI 相關的原生 API 是不被允許的,因為在 web 頁面里操作原生的 GUI 資源是非常危險的,而且容易造成資源泄露。 如果你想在 web 頁面里使用 GUI 操作,其對應的渲染進程必須與主進程進行通訊,請求主進程進行相關的 GUI 操作。

主進程與渲染進程通信

那麼進程間如何通訊?

Electron為主進程( main process)和渲染器進程(renderer processes)通信提供了多種實現方式,如可以使用ipcRenderer 和 ipcMain模塊發送消息,使用 remote模塊進行RPC方式通信。

你還可以用 Electron 內的 IPC 機制實現。將數據存在主進程的某個全局變量中,然後在多個渲染進程中使用 remote 模塊來訪問它。

示例代碼:

// 在主進程中
global.sharedObject = {
  someProperty: 'default value'
}
// 在第一個頁面中
require('electron').remote.getGlobal('sharedObject').someProperty = 'new value'
// 在第二個頁面中
console.log(require('electron').remote.getGlobal('sharedObject').someProperty)

使用Electron的API

Electron在主進程和渲染進程中提供了大量API去幫助開發桌面應用程序, 在主進程和渲染進程中,你可以通過require的方式將其包含在模塊中以此,獲取Electron的API

const electron = require('electron')

所有Electron的API都被指派給一種進程類型。 許多API只能被用於主進程或渲染進程中,但其中一些API可以同時在上述兩種進程中使用。 每一個API的文檔都將聲明你可以在哪種進程中使用該API。

Electron中的窗口是使用BrowserWindow類型創建的一個實例, 它只能在主進程中使用。

// 這樣寫在主進程會有用,但是在渲染進程中會提示'未定義'
const { BrowserWindow } = require('electron')

const win = new BrowserWindow()

因為進程之間的通信是被允許的, 所以渲染進程可以調用主進程來執行任務。 Electron通過remote模塊暴露一些通常只能在主進程中獲取到的API。 為了在渲染進程中創建一個BrowserWindow的實例,通常使用remote模塊為中間件:

// 這樣寫在渲染進程中時行得通的,但是在主進程中是'未定義'
const { remote } = require('electron')
const { BrowserWindow } = remote

const win = new BrowserWindow()

使用Node.js的API

Electron同時在主進程和渲染進程中對Node.js 暴露了所有的接口。 這裡有兩個重要的定義:

1)所有在Node.js可以使用的API,在Electron中同樣可以使用。 在Electron中調用如下代碼是有用的:

const fs = require('fs')

const root = fs.readdirSync('/')

// 這會打印出磁盤根級別的所有文件
// 同時包含'/'和'C:\'。
console.log(root)

2)你可以在你的應用程序中使用Node.js的模塊。 選擇您最喜歡的 npm 模塊。 npm 提供了目前世界上最大的開源代碼庫,那裡包含良好的維護、經過測試的代碼,提供給服務器應用程序的特色功能也提供給Electron。

例如,在你的應用程序中要使用官方的AWS SDK,你需要首先安裝它的依賴:

npm install --save aws-sdk

然後在你的Electron應用中,通過require引入並使用該模塊,就像構建Node.js應用程序那樣:

// 準備好被使用的S3 client模塊
const S3 = require('aws-sdk/clients/s3')

有一個非常重要的提示: 原生Node.js模塊 (即指,需要編譯源碼過後才能被使用的模塊) 需要在編譯后才能和Electron一起使用。

最終產品殺青落地

終於搞明白了 Electron 的應用架構,那麼接着就要進入產品的開發階段。比較慶幸的是,ELectron 的UI完全由CSS+HTML組成,這部分可用的框架太多了,我選擇了又老又知名的 BootStarp 框架搭建界面UI,還引用了JS框架JQuery。選擇了 electron-store 作為本地存儲文件,至於最關鍵的Markdown語法解析,對比了一番主流解析框架,最終選擇了 markdown-it。貼一下效果圖:

這款軟件我給他起名為 JustWrite,意思就是現在就寫,也是在督促自己吧,畢竟猶豫徘徊,等於白來。

現在軟件的功能除了包含一鍵發布本地文章加本地圖片到博客園、CSDN、SegmentFault、掘金、開源中國等平台,我還打算將他打造為一個體驗不錯的Markdown寫作軟件。現在你閱讀的這篇文章,就是我使用 JustWrite 書寫的,使用的字體是我個人喜歡的幼圓體,除此之外,還有六款風格迥異的字體可以切換使用。字號也是可以動態放大或者縮小,還可以關閉右側預覽,專註於寫作,如下圖所示:

這些截圖是我截屏后使用快捷鍵Ctrl+V一鍵粘貼的,圖片會自動放到當前md文件所在目錄下的picture文件夾內。

關於 JustWrite 從構思到實踐的心路歷程大致就以上這些了,這次開發 JustWrite 也讓我過了一把產品經理的癮,基本已經滿足了我的日常需求。如果你有更好的想法和創意也可以告訴我,說不定第二天就會實現了。

Github:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※公開收購3c價格,不怕被賤賣!

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

不只是換殼上市!EC-05 與 Gogoro S2 實測比較

EC-05 由台灣山葉(YAMAHA)和 Gogoro 聯手打造,動力系統採用與 Gogoro S2 相同的 G2 鋁合金水冷永磁同步馬達(S-Version),許多人不禁疑問 EC-05 和 Gogoro S2 到底有什麼差別?難道只是換了一個車殼?更別說 EC-05 的價格還比 Gogoro S2 貴了 2,820 元。為了比較其中的差別,《科技新報》試駕了 EC-05 和 2020 年式的 Gogoro S2,帶來兩台車的第一手觀察。

EC-05 配備嵌合式環型頭燈,能在夜間騎乘時避免光線發散,確保前方視野的明亮程度。Gogoro S2 同樣配備環型頭燈,不過形狀略有不同,和一般機車一樣完全外露。EC-05 的後照鏡為菱形設計,視野較為寬廣,Gogoro S2 的後照鏡是圓形,視野較狹窄,而且騎乘者容易被自己的身體擋住部分視線。

Gogoro S2 前方配備了多功能置物箱,可以擺放手機和手套等小型物品,也能做為飲料置杯架使用,EC-05 就沒有這項裝置。Gogoro S2 的前方置物箱內附 USB 充電插槽,EC-05 的 USB 插孔則位於後座的置物箱。EC-05 使用無線智慧鑰匙,靠近車輛後按壓鑰匙的灰色部分進行解鎖。Gogoro S2 則使用 iQ system 智慧鑰匙卡,只要輕觸 S 標誌就能解鎖。

EC-05(左)配備嵌合式環型頭燈,Gogoro S2 的頭燈則外露。

EC-05 後照鏡呈菱形,視野較寬廣。

Gogoro S2 的後照鏡為圓形。

Gogoro S2 配備多功能置物箱,並附有 USB 充電插槽。

EC-05 的 USB 充電插槽位於後置物箱內側。

EC-05 以無線智慧鑰匙解鎖。

試乘時騎行陽明山的山路,EC-05 的穩定度和避震能力優於 Gogoro S2,在路面凹凸不平時較不容易顛簸,雙載騎乘時的差距更加明顯。EC-05 的座墊將後座的後方略為墊高,讓座墊呈現「山」字型的結構,側邊則採切削設計,讓騎乘者的腿部更為舒適。Gogoro S2 的後座較為平滑,中間使用皮質材質並標有「S」字樣。雖然乍看之下差異不大,但實際騎乘時後座乘客的感受明顯不同。

EC-05 的乘客在機車減速和加速時都更容易維持穩定,比較不會有往後飛出去或往前貼到前方騎乘者身上的狀況,即使長時間乘坐也不會滑動。Gogoro S2 的後座把手較為平直,EC-05 的把手彎曲向上,對於手不夠長的乘客更容易抓握。相較於 Gogoro S2,EC-05 能給予後座乘客更安穩的乘坐體驗。

EC-05 的座墊呈現「山」字型的凸起,讓後座乘客乘坐時更穩定。

Gogoro S2 的座墊較為平滑。

EC-05 與 Gogoro S2 座墊比較。

EC-05 的後座把手彎曲向上,較容易抓握。

Gogoro S2 的後座把手平直,手沒那麼長的後座乘客較難抓握。

EC-05(右)與 Gogoro S2 的後座把手差異從車尾可以明顯看出。

由於 EC-05 與 Gogoro S2 採用相同的動力系統,因此性能上的數據相差無幾。EC-05 最大馬力為 10hp,安全極速達到時速 90 公里,靜止加速到時速 50 公里需要 3.9 秒。Gogoro S2 最大馬力為 10.18hp,安全極速達到時速 92 公里,靜止加速到時速 50 公里僅需 3.8 秒,各方面都是 Gogoro S2 稍勝。

相較於 Gogoro S2,EC-05 的儀表板略為調整傾斜角度並向前方移動,用以減少騎乘者所需的視線移動。EC-05 的腳踏板具有菱形的凸起幫助止滑,讓騎乘時腳的位置可以更穩定。Gogoro S2 的腳踏板則是完全平滑,並有著「gogoro」的字樣。

EC-05 在車及座墊高度上也有更動,車高為 1,180 mm 略高於 Gogoro S2 的 1,080 mm,座高則以 768 mm 略低於 Gogoro S2 的 780 mm。調整過後 EC-05 在龍頭和座墊間的空間增加,騎乘時較為舒適也不容易意外按壓到喇叭等按鍵。不過 EC-05 的龍頭相較 Gogoro S2 略為沉重,力氣較小的使用者操控時可能會稍微費力。

EC-05 的腳踏板具有止滑的菱形格紋。

Gogoro S2 的腳踏板較為平滑。

身高 176 公分的騎乘者與 EC-05 的比例。

身高 156 公分的騎乘者與 EC-05 的比例。

YAMAHA 的設計理念標榜「人機官能」,也就是在設計時結合機械結構和人體工學。這樣的概念讓 EC-05 看似與 Gogoro S2 僅有微幅的差異,但這些細節卻在騎乘時能發揮不成比例的效果。至於這樣的差別值不值得多花 2,820 元購買,那就要看個人是否在意這些細節帶來的感受。

EC-05 提供藍灰色、深黑色、深藍灰色和白銀色 4 種顏色讓消費者選擇,定價為台幣 99,800 元。Gogoro S2 僅有灰黑色一個款式,定價為台幣 96,980 元。

(合作媒體:。圖片來源:)

延伸閱讀:

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※高價收購3C產品,價格不怕你比較

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

3c收購,鏡頭 收購有可能以全新價回收嗎?

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

面試官:你連RESTful都不知道我怎麼敢要你?

目錄

面試官:了解RESTful嗎?

我:聽說過。

面試官:那什麼是RESTful?

我:就是用起來很規範,挺好的

面試官:是RESTful挺好的,還是自我感覺挺好的

我:都挺好的。

面試官:… 把門關上。

我:…. 要幹嘛?先關上再說。

面試官:我說出去把門關上。

我:what ?,奪門而去

@

01 前言

回歸正題,看過很多RESTful相關的文章總結,參齊不齊,結合工作中的使用,非常有必要歸納一下關於RESTful架構方式了,RESTful只是一種架構方式的約束,給出一種約定的標準,完全嚴格遵守RESTful標準並不是很多,也沒有必要。但是在實際運用中,有RESTful標準可以參考,是十分有必要的。

實際上在工作中對api接口規範、命名規則、返回值、授權驗證等進行一定的約束,一般的項目api只要易測試、足夠安全、風格一致可讀性強、沒有歧義調用方便我覺得已經足夠了,接口是給開發人員看的,也不是給普通用戶去調用。

02 RESTful的來源

REST:Representational State Transfer(表象層狀態轉變),如果沒聽說過REST,你一定以為是rest這個單詞,剛開始我也是這樣認為的,後來發現是這三個單詞的縮寫,即使知道了這三個單詞理解起來仍然非常晦澀難懂。如何理解RESTful架構,最好的辦法就是深刻理解消化Representational State Transfer這三個單詞到底意味着什麼。

1.每一個URI代表一種資源;

2.客戶端和服務器之間,傳遞這種資源的某種表現層;

3.客戶端通過四個HTTP動詞(get、post、put、delete),對服務器端資源進行操作,實現”表現層狀態轉化”。

是由美國計算機科學家Roy Fielding(百度百科沒有介紹,真是尷尬了)。Adobe首席科學家、Http協議的首要作者之一、Apache項目聯合創始人。

03 RESTful6大原則

REST之父Roy Fielding在論文中闡述REST架構的6大原則。

1. C-S架構

數據的存儲在Server端,Client端只需使用就行。兩端徹底分離的好處使client端代碼的可移植性變強,Server端的拓展性變強。兩端單獨開發,互不干擾。

2. 無狀態

http請求本身就是無狀態的,基於C-S架構,客戶端的每一次請求帶有充分的信息能夠讓服務端識別。請求所需的一些信息都包含在URL的查詢參數、header、body,服務端能夠根據請求的各種參數,無需保存客戶端的狀態,將響應正確返回給客戶端。無狀態的特徵大大提高的服務端的健壯性和可拓展性。

當然這總無狀態性的約束也是有缺點的,客戶端的每一次請求都必須帶上相同重複的信息確定自己的身份和狀態(這也是必須的),造成傳輸數據的冗餘性,但這種確定對於性能和使用來說,幾乎是忽略不計的。

3.統一的接口

這個才是REST架構的核心,統一的接口對於RESTful服務非常重要。客戶端只需要關注實現接口就可以,接口的可讀性加強,使用人員方便調用。

4.一致的數據格式

服務端返回的數據格式要麼是XML,要麼是Json(獲取數據),或者直接返回狀態碼,有興趣的可以看看博客園的開放平台的操作數據的api,post、put、patch都是返回的一個狀態碼 。

自我描述的信息,每項數據應該是可以自我描述的,方便代碼去處理和解析其中的內容。比如通過HTTP返回的數據裏面有 [MIME type ]信息,我們從MIME type裏面可以知道數據的具體格式,是圖片,視頻還是JSON,客戶端通過body內容、查詢串參數、請求頭和URI(資源名稱)來傳送狀態。服務端通過body內容,響應碼和響應頭傳送狀態給客戶端。這項技術被稱為超媒體(或超文本鏈接)。

除了上述內容外,HATEOS也意味着,必要的時候鏈接也可被包含在返回的body(或頭部)中,以提供URI來檢索對象本身或關聯對象。下文將對此進行更詳細的闡述。

如請求一條微博信息,服務端響應信息應該包含這條微博相關的其他URL,客戶端可以進一步利用這些URL發起請求獲取感興趣的信息,再如分頁可以從第一頁的返回數據中獲取下一頁的URT也是基於這個原理。

4.系統分層

客戶端通常無法表明自己是直接還是間接與端服務器進行連接,分層時同樣要考慮安全策略。

5.可緩存

在萬維網上,客戶端可以緩存頁面的響應內容。因此響應都應隱式或顯式的定義為可緩存的,若不可緩存則要避免客戶端在多次請求後用舊數據或臟數據來響應。管理得當的緩存會部分地或完全地除去客戶端和服務端之間的交互,進一步改善性能和延展性。

6.按需編碼、可定製代碼(可選)

服務端可選擇臨時給客戶端下發一些功能代碼讓客戶端來執行,從而定製和擴展客戶端的某些功能。比如服務端可以返回一些 Javascript 代碼讓客戶端執行,去實現某些特定的功能。
提示:REST架構中的設計準則中,只有按需編碼為可選項。如果某個服務違反了其他任意一項準則,嚴格意思上不能稱之為RESTful風格。

03 RESTful的7個最佳實踐

1. 版本

如github開放平台
https://developer.github.com/v3/

就是將版本放在url,簡潔明了,這個只有用了才知道,一般的項目加版本v1,v2,v3?好吧,這個加版本估計只有大公司大項目才會去使用,說出來不怕尷尬,我真沒用過。有的會將版本號放在header裏面,但是不如url直接了當。

https://example.com/api/v1/

2.參數命名規範

query parameter可以採用駝峰命名法,也可以採用下劃線命名的方式,推薦採用下劃線命名的方式,據說後者比前者的識別度要高,可能是用的人多了吧,因人而異,因團隊規範而異吧。

https://example.com/api/users/today_login 獲取今天登陸的用戶 
https://example.com/api/users/today_login&sort=login_desc 獲取今天登陸的用戶、登陸時間降序排列

3.url命名規範

API 命名應該採用約定俗成的方式,保持簡潔明了。在RESTful架構中,每個url代表一種資源所以url中不能有動詞,只能有名詞,並且名詞中也應該使用複數。實現者應使用相應的Http動詞GET、POST、PUT、PATCH、DELETE、HEAD來操作這些資源即可

不規範的的url,冗餘沒有意義,形式不固定,不同的開發者還需要了解文檔才能調用。

https://example.com/api/getallUsers GET 獲取所有用戶 
https://example.com/api/getuser/1 GET 獲取標識為1用戶信息 
https://example.com/api/user/delete/1 GET/POST 刪除標識為1用戶信息 
https://example.com/api/updateUser/1 POST 更新標識為1用戶信息 
https://example.com/api/User/add POST 添加新的用戶

規範后的RESTful風格的url,形式固定,可讀性強,根據users名詞和http動詞就可以操作這些資源

https://example.com/api/users GET 獲取所有用戶信息 
https://example.com/api/users/1 GET 獲取標識為1用戶信息 
https://example.com/api/users/1 DELETE 刪除標識為1用戶信息 
https://example.com/api/users/1 Patch 更新標識為1用戶部分信息,包含在body中 
https://example.com/api/users POST 添加新的用戶

4. 統一返回數據格式

對於合法的請求應該統一返回數據格式,這裏演示的是json

  • code——包含一個整數類型的HTTP響應狀態碼。
  • status——包含文本:”success”,”fail”或”error”。HTTP狀態響應碼在500-599之間為”fail”,在400-499之間為”error”,其它均為”success”(例如:響應狀態碼為1XX、2XX和3XX)。這個根據實際情況其實是可要可不要的。
  • message——當狀態值為”fail”和”error”時有效,用於显示錯誤信息。參照國際化(il8n)標準,它可以包含信息號或者編碼,可以只包含其中一個,或者同時包含並用分隔符隔開。
  • data——包含響應的body。當狀態值為”fail”或”error”時,data僅包含錯誤原因或異常名稱、或者null也是可以的

返回成功的響應json格式

{
  "code": 200,
  "message": "success",
  "data": {
    "userName": "123456",
    "age": 16,
    "address": "beijing"
  }
}

返回失敗的響應json格式

{
  "code": 401,
  "message": "error  message",
  "data": null
}

下面這個ApiResult的泛型類是在項目中用到的,拓展性強,使用方便。返回值使用統一的 ApiResult 或 ApiResult
錯誤返回 使用 ApiResult.Error 進行返回; 成功返回,要求使用 ApiResult.Ok 進行返回

public class ApiResult: ApiResult
    {
        public new static ApiResult<T> Error(string message)
        {
            return new ApiResult<T>
            {
                Code = 1,
                Message = message,
            };
        }
        [JsonProperty("data")]
        public T Data { get; set; }
    }
    public class ApiResult
    {
        public static ApiResult Error(string message)
        {
            return new ApiResult
            {
                Code = 1,
                Message = message,
            };
        }

        public static ApiResult<T> Ok<T>(T data)
        {
            return new ApiResult<T>()
            {
                Code = 0,
                Message = "",
                Data = data
            };
        }
        /// <summary>
        /// 0 是 正常 1 是有錯誤
        /// </summary>
        [JsonProperty("code")]
        public int Code { get; set; }
        [JsonProperty("msg")]
        public string Message { get; set; }

        [JsonIgnore]
        public bool IsSuccess => Code == 0;
    }

5. http狀態碼

在之前開發的xamarin android博客園客戶端的時候,patch、delete、post操作時body響應裏面沒有任何信息,僅僅只有http status code。HTTP狀態碼本身就有足夠的含義,根據http status code就可以知道刪除、添加、修改等是否成功。(ps:有點linux設計的味道哦,沒有返回消息就是最好的消息,表示已經成功了)服務段向用戶返回這些狀態碼並不是一個強制性的約束。簡單點說你可以指定這些狀態,但是不是強制的。常用HTTP狀態碼對照表
HTTP狀態碼也是有規律的

  • 1**請求未成功
  • 2**請求成功、表示成功處理了請求的狀態代碼。
  • 3**請求被重定向、表示要完成請求,需要進一步操作。 通常,這些狀態代碼用來重定向。
  • 4** 請求錯誤這些狀態代碼錶示請求可能出錯,妨礙了服務器的處理。
  • 5**(服務器錯誤)這些狀態代碼錶示服務器在嘗試處理請求時發生內部錯誤。 這些錯誤可能是服務器本身的錯誤,而不是請求出錯。

    6. 合理使用query parameter

    在請求數據時,客戶端經常會對數據進行過濾和分頁等要求,而這些參數推薦採用HTTP Query Parameter的方式實現

比如設計一個最近登陸的所有用戶
https://example.com/api/users?recently_login_day=3
搜索用戶,並按照註冊時間降序
https://example.com/api/users?recently_login_day=3
搜索用戶,並按照註冊時間升序、活躍度降序
https://example.com/api/users?q=key&sort=create_title_asc,liveness_desc
關於分頁,看看博客園開放平台分頁獲取精華區博文列表
https://api.cnblogs.com/api/blogposts/@picked?pageIndex={pageIndex}&pageSize={pageSize} 
返回示例: 
[ 
{ 
“Id”: 1, 
“Title”: “sample string 2”, 
“Url”: “sample string 3”, 
“Description”: “sample string 4”, 
“Author”: “sample string 5”, 
“BlogApp”: “sample string 6”, 
“Avatar”: “sample string 7”, 
“PostDate”: “2017-06-25T20:13:38.892135+08:00”, 
“ViewCount”: 9, 
“CommentCount”: 10, 
“DiggCount”: 11 
}, 
{ 
“Id”: 1, 
“Title”: “sample string 2”, 
“Url”: “sample string 3”, 
“Description”: “sample string 4”, 
“Author”: “sample string 5”, 
“BlogApp”: “sample string 6”, 
“Avatar”: “sample string 7”, 
“PostDate”: “2017-06-25T20:13:38.892135+08:00”, 
“ViewCount”: 9, 
“CommentCount”: 10, 
“DiggCount”: 11 
} 
]

7. 多表、多參數連接查詢如何設計URL

這是一個比較頭痛的問題,在做單個實體的查詢比較容易和規範操作,但是在實際的API並不是這麼簡單而已,這其中常常會設計到多表連接、多條件篩選、排序等。
比如我想查詢一個獲取在6月份的訂單中大於500元的且用戶地址是北京,用戶年齡在22歲到40歲、購買金額降序排列的訂單列表

https://example.com/api/orders?order_month=6&order_amount_greater=500&address_city=北京&sort=order_amount_desc&age_min=22&age_max=40

從這個URL上看,參數眾多、調用起來還得一個一個仔細對着,而且API本身非常不容易維護,命名看起來不是很容易,不能太長,也不能太隨意。

在.net WebAPI總我們可以使用屬性路由,屬性路由就是講路由附加到特定的控制器或操作方法上裝飾Controll及其使用[Route]屬性定義路由的方法稱為屬性路由。

這種好處就是可以精準地控制URL,而不是基於約定的路由,簡直就是為這種多表查詢量身定製似的的。 從webapi 2開發,現在是RESTful API開發中最推薦的路由類型。
我們可以在Controll中標記Route

[Route(“api/orders/{address}/{month}”)] 

Action中的查詢參數就只有金額、排序、年齡。減少了查詢參數、API的可讀性和可維護行增強了。

https://example.com/api/orders/beijing/6?order_amount_greater=500&sort=order_amount_desc&age_min=22&age_max=40

這種屬性路由比如在博客園開放的API也有這方面的應用,如獲取個人博客隨筆列表

請求方式:GET 
請求地址:https://api.cnblogs.com/api/blogs/{blogApp}/posts?pageIndex={pageIndex} 
(ps:blogApp:博客名)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

3c收購,鏡頭 收購有可能以全新價回收嗎?

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

賣IPHONE,iPhone回收,舊換新!教你怎麼賣才划算?

小白理解安卓虛擬機以及華為的’諾亞方舟’

虛擬機提到虛擬機,大家可能第一反應就是java中好像有虛擬機這個玩意。但是安卓中的虛擬機是什麼呢?是和java一樣的嗎?那麼我們先來了解一下java中的JVM!

JVM,搞java的肯定對它了解不少。JVM本質上就是一個軟件,是計算機硬件的一層軟件抽象,在這之上才幹夠運行Java程序,JAVA在編譯後會生成相似於彙編語言的JVM字節碼,與C語言編譯后產生的彙編語言不同的是,C編譯成的彙編語言會直接在硬件上跑。但JAVA編譯後生成的字節碼是在JVM上跑,須要由JVM把字節碼翻譯成機器指令。才幹使JAVA程序跑起來。JVM運行在操作系統上,屏蔽了底層實現的差異。從而有了JAVA吹噓的平台獨立性和Write Once Run Anywhere。依據JVM規範實現的詳細虛擬機有幾十種,主流的JVM包括Hotspot、Jikes RVM等。都是用C/C++和彙編編寫的,每一個JRE編譯的時候針對每一個平台編譯。因此下載JRE(JVM、Java核心類庫和支持文件)的時候是分平台的,JVM的作用是把平台無關的.class裏面的字節碼翻譯成平台相關的機器碼,來實現跨平台。

說白了,簡單點,就是:

                                                                      Java

                                                                      .java文件 -> .class文件 -> .jar文件

最後執行是class文件,有的會被再次打包成jar文件。

了解了這些之後,我們再去了解Android 中的虛擬機。

一、Dalvik虛擬機

Dalvik虛擬機( Dalvik Virtual Machine ),簡稱Dalvik VM或者DVM。這就是Android中的虛擬機。最初它的產生,是因為Google為了解決與Oracle之間關於Java相關專利和授權的糾紛,開發了DVM。

Android既然存在虛擬機,肯定也是在這個DVM上執行的。它的執行流程和JVM很像:

                                                                       Android

                                                                      .java文件 –> .class文件 -> .dex文件->.apk

DVM執行的是.dex格式文件,JVM執行的是.class文件,android程序編譯完之後生產.class文件,然後,dex工具會把.class文件處理成.dex文件,然後把資源文件和.dex文件等打包成.apk文件,apk就是android package的意思。

除了上面所說的,專利授權的原因除外,其實還有因為如下原因:

    dvm是基於寄存器的虛擬機,而jvm是基於虛擬棧的虛擬機。寄存器存取速度比棧快得多,dvm可以根據硬件實現最大的優化,比較適合移動設備。

    class文件存在很多的冗餘信息,dex工具會去除冗餘信息,並把所有的.class文件整合到.dex文件中,減少了I/O操作,提高了類的查找速度。

不光是上面這些差異,還有運行環境。  

   Dalvik : 一個應用啟動都運行一個單獨的虛擬機運行在一個單獨的進程中

   JVM: 只能運行一個實例, 也就是所有應用都運行在同一個JVM中

 

 這個是早先的安卓虛擬機,運行速度還是相當慢的。基於寄存器的虛擬機允許更快的執行時間,但代價是編譯后的程序更大。於是新的Dex字節碼格式odex產生了。它的作用等同於dex,只不過是dex優化后的格式。在App安裝的過程中,會通過Socket向/system/bin/install進程發送dex_opt的指令,對Dex文件進行優化。在DexClassLoader動態加載Dex文件時,也會進行Dex的優化,形成odex文件。

 

為了適應硬件速度的提升,隨後在Android 2.2的DVM中加入了JIT 編譯器(Just-In-Time Compiler)。Dalvik 使用 JIT 進行即時編譯,藉助 Java HotSpot VM,JIT 編譯器可以對執行次數頻繁的 dex/odex 代碼進行編譯與優化,將 dex/odex 中的 Dalvik Code(Smali 指令集)翻譯成相當精簡的 Native Code 去執行,JIT 的引入使得 Dalvik 的性能提升了 3~6 倍。

JIT編譯器的引入,提升了安裝速度,減少了佔用的空間,但隨之帶來的問題就是:多個dex加載會非常慢;JIT中的解釋器解釋的字節碼會帶來CPU和時間的消耗;還有熱點代碼的Monitor一直在運行帶來電量的損耗。

 

 這種情況下,手機動不動就卡是難以避免的。相信各位如果那時候用着Android手機,一定印象非常深刻。因為並不是那麼好用。

這樣的狀況一直持續到Andorid 4.4,帶來了全新的虛擬機運行環境 ART(Android RunTime)的預覽版和全新的編譯策略 AOT(Ahead-of-time)。但那時候。 ART 是和 Dalvik 共存的,用戶可以在兩者之間進行選擇(感覺很奇怪,作為一個愛好者,我當時看到這個東西可以切換都是不曉得是什麼玩意,用戶可都是小白啊,沒有必要共存的吧)。在Android 5.0的時候,ART 全面取代 Dalvik 成為 Android 虛擬機運行環境,至此。Dalvik 退出歷史舞台,AOT 也成為唯一的編譯模式。

二、ART 

AOT 和 JIT 的不同之處在於:JIT 是在運行時進行編譯,是動態編譯,並且每次運行程序的時候都需要對 odex 重新進行編譯;而 AOT 是靜態編譯,應用在安裝的時候會啟動 dex2oat 通過靜態編譯的方式,來將所有的dex文件(包括Multidex)編譯oat文件,編譯完后的oat其實是一個標準的ELF文件,只是相對於普通的ELF文件多加了oat data section以及oat exec section這兩個段而已。(這兩個段裏面主要保存了兩種信息:Dex的文件信息以及類信息和Dex文件編譯之後的機器碼)。預編譯成 ELF 文件,每次運行程序的時候不用重新編譯,是真正意義上的本地應用。運行的文件格式也從odex轉換成了oat格式。

 

其實在Android5.0的時候我們能夠明顯感覺手機好用很多就是因為這個原因,從根本上換掉了那種存在着無法解決弊端的虛擬機。在 Android 5.x 和 6.x 的機器上,系統每次 OTA 升級完成重啟的時候都會有個應用優化的過程,這個過程就是剛才所說的 dex2oat 過程,這個過程比較耗時並且會佔用額外的存儲空間。

AOT 模式的預編譯解決了應用啟動和運行速度和耗資源(電等)問題的同時也帶來了另外兩個問題:

      1、應用安裝和系統升級之後的應用優化比較耗時,並且會更耗時間。因為系統和apk都是越來越大的。

      2、優化后的文件會佔用額外的存儲空間

在經過了兩個Android大版本的穩定后,在Android7.0又再次迎來了JIT的 回歸。

JIT的回歸,可不是把AOT模式給取代了,而是形成 了AOT/JIT 混合編譯模式,這種模式至今仍在使用

應用在安裝的時候 dex 不會被編譯。

應用在運行時 dex 文件先通過解析器(Interpreter)後會被直接執行(這一步驟跟 Android 2.2 – Android 4.4之前的行為一致),與此同時,熱點函數(Hot Code)會被識別並被 JIT 編譯后存儲在 jit code cache 中並生成 profile 文件以記錄熱點函數的信息。

手機進入 IDLE(空閑) 或者 Charging(充電) 狀態的時候,系統會掃描 App 目錄下的 profile 文件並執行 AOT 過程進行編譯。

(Profile文件會在JIT運行的過程中生成:每個APP都會有自己的Profile文件,保存在App本身的Local Storage中。Profile會保存所調用的類以及函數的Index,通過profman工具進行分析生成)

 

 

個人理解:哪種模式擅長干什麼就讓他去干什麼。

混合編譯模式綜合了 AOT 和 JIT 的各種優點,使得應用在安裝速度加快的同時,運行速度、存儲空間和耗電量等指標都得到了優化。

之前一直在說流暢,真的流暢在Android7.0上才感受到了些許。Android7.0系統也被用了相當長的一段時間。之後的Android8.0和Android9.0都是對各方面的優化,例如編譯文件、編譯器、GC。。

其中,值得一提的是華為的方舟編譯器。

  • 首先會判斷該設備支不支持方舟編譯器,如果支持,則從應用商店下發方舟版本的包
  • 方舟編譯器會把dex文件通過自己的IR翻譯方舟格式的機器碼,據資料說也是一個ELF文件,但是會增加一些段,猜測是Dex中類信息相關的段
  • 通過這種方式,來消除Java與JNI之間的通信的損耗,以及提升運行時的效率
  • 在方舟內部,還重新完善了GC算法,使得GC的頻率大大降低,減少應用卡頓的現象
  • 目前方舟只支持64位的So,並且對於加殼的So會出現一些問題。

方舟編譯器適配的應用,下載手機上都是方舟版本的包,特製的包用方舟編譯器編譯效率大大提升,之後直接執行就可以了,直接略過了在ART虛擬機上預編譯的過程。這樣的結果是很完美的,但是卻也沒辦法跳過一個弊端。那就是生態。還是不管是安卓還是iOS,這麼多年的時間沉澱中,他們的生態系統早就達到了一個非常完善的地步。安卓和iOS應用已經多達上千萬,而方舟適配應用的數量還非常有限。

谷歌宣布將停止對華為提供安卓系統更新之後,華為曝光了自主研發的鴻蒙操作系統。當時網友各種力挺。不過後來,華為董事長梁華在談及鴻蒙系統時稱,鴻蒙系統是為物聯網開發的,用於自動駕駛、遠程醫療等低時延場景。鴻蒙系統是不是兩手準備我們不得而知。但是,一個操作系統最重要的就是它的生態環境。縱觀華為現在的整個格局,目的非常明確,用方舟編譯器來擴大自己的用戶群體。當用戶的基數足夠龐大時,可以隨時隨地建立一個完善的生態系統。如果在未來某一天,Android全面限制華為的使用之後,在這危機關頭鴻蒙系統還是很有可能扛起國產手機的一面大旗。哪怕不是鴻蒙,我們也需要這樣一個生態不是嗎?

最初,突然去了解Android中的虛擬機,一個是想要明白到底Android中的虛擬機和JVM是不是一回事,還有就是想要明白華為發布方舟編譯器到底快到了哪裡。

上述相關資料均來自網絡,侵權必刪。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※公開收購3c價格,不怕被賤賣!

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

地理文本處理技術在高德的演進(上)

一、背景

地圖App的功能可以簡單概括為定位,搜索,導航三部分,分別解決在哪裡,去哪裡,和怎麼去的問題。高德地圖的搜索場景下,輸入的是,地理相關的檢索query,用戶位置,App圖面等信息,輸出的是,用戶想要的POI。如何能夠更加精準地找到用戶想要的POI,提高滿意度,是評價搜索效果的最關鍵指標。

一個搜索引擎通常可以拆分成query分析、召回、排序三個部分,query分析主要是嘗試理解query表達的含義,為召回和排序給予指導。

地圖搜索的query分析不僅包括通用搜索下的分詞,成分分析,同義詞,糾錯等通用NLP技術,還包括城市分析,wherewhat分析,路徑規劃分析等特定的意圖理解方式。

常見的一些地圖場景下的query意圖表達如下:

query分析是搜索引擎中策略密集的場景,通常會應用NLP領域的各種技術。地圖場景下的query分析,只需要處理地理相關的文本,多樣性不如網頁搜索,看起來會簡單一些。但是,地理文本通常比較短,並且用戶大部分的需求是唯一少量結果,要求精準度非常高,如何能夠做好地圖場景下的文本分析,並提升搜索結果的質量,是充滿挑戰的。

二、整體技術架構

搜索架構

類似於通用檢索的架構,地圖的檢索架構包括query分析,召回,排序三個主要部分。先驗的,用戶的輸入信息可以理解為多種意圖的表達,同時下發請求嘗試獲取檢索結果。后驗的,拿到每種意圖的檢索結果時,進行綜合判斷,選擇效果最好的那個。

query分析流程

具體的意圖理解可分為基礎query分析和應用query分析兩部分,基礎query分析主要是使用一些通用的NLP技術對query進行理解,包括分析,成分分析,省略,同義詞,糾錯等。應用query分析主要是針對地圖場景里的特定問題,包括分析用戶目標城市,是否是where+what表達,是否是從A到B的路徑規劃需求表達等。

整體技術演進

在地里文本處理上整體的技術演進經歷了規則為主,到逐步引入機器學習,到機器學習全面應用的過程。由於搜索模塊是一個高併發的線上服務,對於深度模型的引入有比較苛刻的條件,但隨着性能問題逐漸被解決,我們從各個子方向逐步引入深度學習的技術,進行新一輪的效果提升。

NLP領域技術在最近幾年取得了日新月異的發展,bert,XLNet等模型相繼霸榜,我們逐步統一化各個query分析子任務,使用統一的向量表示對進行用戶需求進行表達,同時進行seq2seq的多任務學習,在效果進一步提升的基礎上,也能夠保證系統不會過於臃腫。

本文就高德地圖搜索的地理文本處理,介紹相關的技術在過去幾年的演進。我們將選取一些點分上下兩篇進行介紹,上篇主要介紹搜索引擎中一些通用的query分析技術,包括糾錯,改寫和省略。下篇着重介紹地圖場景中特有query分析技術,包括城市分析,wherewhat分析,路徑規劃。

三、通用query分析技術演進

3.1 糾錯

在搜索引擎中,用戶輸入的檢索詞(query)經常會出現拼寫錯誤。如果直接對錯誤的query進行檢索,往往不會得到用戶想要的結果。因此不管是通用搜索引擎還是垂直搜索引擎,都會對用戶的query進行糾錯,最大概率獲得用戶想搜的query。

在目前的地圖搜索中,約有6%-10%的用戶請求會輸入錯誤,所以query糾錯在地圖搜索中是一個很重要的模塊,能夠極大的提升用戶搜索體驗。

在搜索引擎中,低頻和中長尾問題往往比較難解決,也是糾錯模塊面臨的主要問題。另外,地圖搜索和通用搜索,存在一個明顯的差異,地圖搜索query結構化比較突出,query中的片段往往包含一定的位置信息,如何利用好query中的結構化信息,更好地識別用戶意圖,是地圖糾錯獨有的挑戰。

常見錯誤分類

(1) 拼音相同或者相近,例如: 盤橋物流園-潘橋物流園
(2) 字形相近,例如: 河北冒黎-河北昌黎
(3) 多字或者漏字,例如: 泉州州頂街-泉州頂街

糾錯現狀

原始糾錯模塊包括多種召回方式,如:

拼音糾錯:主要解決短query的拼音糾錯問題,拼音完全相同或者模糊音作為糾錯候選。
拼寫糾錯:也叫形近字糾錯,通過遍歷替換形近字,用query熱度過濾,加入候選。
組合糾錯:通過翻譯模型進行糾錯替換,資源主要是通過query對齊挖掘的各種替換資源。

組合糾錯翻譯模型計算公式:

其中p(f)是語言模型,p(f|e)是替換模型。

問題1:召回方式存在缺陷。目前query糾錯模塊主要召回策略包括拼音召回、形近字召回,以及替換資源召回。對於低頻case,解決能力有限。

問題2:排序方式不合理。糾錯按照召回方式分為幾個獨立的模塊,分別完成相應的召回和排序,不合理。

技術改造

改造1:基於空間關係的實體糾錯
原始的糾錯主要是基於用戶session挖掘片段替換資源,所以對於低頻問題解決能力有限。但是長尾問題往往集中在低頻,所以低頻問題是當前的痛點。

地圖搜索與通用搜索引擎有個很大的區別在於,地圖搜索query比較結構化,例如北京市朝陽區阜榮街10號首開廣場。我們可以對query進行結構化切分(也就是地圖中成分分析的工作),得到這樣一種帶有類別的結構化描述,北京市【城市】朝陽區【區縣】阜榮街【道路】10號【門址後綴】首開廣場【通用實體】。

同時,我們擁有權威的地理知識數據,利用權威化的地理實體庫進行前綴樹+後綴樹的索引建庫,提取疑似糾錯的部分在索引庫中進行拉鏈召回,同時利用實體庫中的邏輯隸屬關係對糾錯結果進行過濾。實踐表明,這種方式對低頻的區劃或者實體的錯誤有着明顯的作用。

基於字根的字形相似度計算

上文提到的排序策略裏面通過字形的編輯距離作為排序的重要特徵,這裏我們開發了一個基於字根的字形相似度計算策略,對於編輯距離的計算更為細化和準確。漢字信息有漢字的字根拆分詞表和漢字的筆畫數。

將一個漢字拆分成多個字根,尋找兩個字的公共字根,根據公共字根筆畫數來計算連個字的相似度。

改造2:排序策略重構

原始的策略召回和排序策略耦合,導致不同的召回鏈路,存在顧此失彼的情況。為了能夠充分發揮各種召回方式的優勢,急需要對召回和排序進行解耦並進行全局排序優化。為此我們增加了排序模塊,將流程分為召回和排序兩階段。

模型選擇

對於這個排序問題,這裏我們參考業界的實踐,使用了基於pair-wise的gbrank進行模型訓練。

樣本建設

通過線上輸出結合人工review的方式構造樣本。

特徵建設
(1) 語義特徵。如統計語言模型。
(2) 熱度特徵。pv,點擊等。
(3) 基礎特徵。編輯距離,切詞和成分特徵,累積分佈特徵等。

這裏解決了糾錯模塊兩個痛點問題,一個是在地圖場景下的大部分低頻糾錯問題。另一個是重構了模塊流程,將召回和排序解耦,充分發揮各個召回鏈路的作用,召回方式更新后只需要重訓排序模型即可,使得模塊更加合理,為後面的深度模型升級打下良好的基礎。後面在這個框架下,我們通過深度模型進行seq2seq的糾錯召回,取得了進一步的收益。

3.2 改寫

糾錯作為query變換的一種方式的召回策略存在諸多限制,對於一些非典型的query變換表達,存在策略的空白。比如query=永城市新農合辦,目標POI是永城市新農合服務大廳。用戶的低頻query,往往得不到較好搜索效果,但其實用戶描述的語義與主poi的高頻query是相似的。

這裏我們提出一種query改寫的思路,可以將低頻query改寫成語義相似的高頻query,以更好地滿足用戶需求多樣性的表達。

這是一個從無到有的實現。用戶表達的query是多樣的,使用規則表達顯然是難以窮盡的,直觀的思路是通過向量的方式召回,但是向量召回的方式很可能出現泛化過多,不適應地圖場景的檢索的問題,這些都是要在實踐過程中需要考慮的問題。

方案

整體看,方案包括召回,排序,過濾,三個階段。

召回階段

我們調研了句子向量表示的幾種方法,選擇了算法簡單,效果和性能可以和CNN,RNN媲美的SIF(Smooth Inverse Frequency)。向量召回可以使用開源的Faiss向量搜索引擎,這裏我們使用了阿里內部的性能更好的的向量檢索引擎。

排序階段
樣本構建
原query與高頻query候選集合,計算語義相似度,選取語義相似度的TOPK,人工標註的訓練樣本。

特徵建設

1.基礎文本特徵
2.編輯距離
3.組合特徵

模型選擇

使用xgboost進行分數回歸

過濾階段
通過向量召回的query過度泛化非常嚴重,為了能夠在地圖場景下進行應用,增加了對齊模型。使用了兩種統計對齊模型giza和fastalign,實驗證明二者效果幾乎一致,但fastalign在性能上好於giza,所以選擇fastalign。

通過對齊概率和非對齊概率,對召回的結果進行進一步過濾,得到精度比較高的結果。

query改寫填補了原始query分析模塊中一些低頻表達無法滿足的空白,區別於同義詞或者糾錯的顯式query變換表達,句子的向量表示是相似query的一種隱式的表達,有其相應的優勢。

向量表示和召回也是深度學習模型逐步開始應用的嘗試。同義詞,改寫,糾錯,作為地圖中query變換主要的三種方式,以往在地圖模塊里比較分散,各司其職,也會有互相重疊的部分。在後續的迭代升級中,我們引入了統一的query變換模型進行改造,在取得收益的同時,也擺脫掉了過去很多規則以及模型耦合造成的歷史包袱。

3.2 省略

在地圖搜索場景里,有很多query包含無效詞,如果用全部query嘗試去召回很可能不能召回有效結果。如廈門市搜”湖裡區縣后高新技術園新捷創運營中心11樓1101室 縣后brt站”。這就需要一種檢索意圖,在不明顯轉義下,使用核心term進行召回目標poi候選集合,當搜索結果無果或者召回較差時起到補充召回的作用。

在省略判斷的過程中存在先驗后驗平衡的問題。省略意圖是一個先驗的判斷,但是期望的結果是能夠進行POI有效召回,和POI的召回字段的現狀密切相關。如何能夠在策略設計的過程中保持先驗的一致性,同時能夠在後驗POI中拿到相對好的效果,是做好省略模塊比較困難的地方。

原始的省略模塊主要是基於規則進行的,規則依賴的主要特徵是上游的成分分析特徵。由於基於規則擬合,模型效果存在比較大的優化空間。另外,由於強依賴成分分析,模型的魯棒性並不好。

技術改造

省略模塊的改造主要完成了規則到crf模型的升級,其中也離線應用了深度學習模型輔助樣本生成。

模型選擇

識別出來query哪些部分是核心哪些部分是可以省略的,是一個序列標註問題。在淺層模型的選型中,顯而易見地,我們使用了crf模型。

特徵建設

term特徵。使用了賦權特徵,詞性,先驗詞典特徵等。
成分特徵。仍然使用成分分析的特徵。
統計特徵。統計片段的左右邊界熵,城市分佈熵等,通過分箱進行離散化。

樣本建設

項目一期我們使用了使用線上策略粗標,外包細標的方式,構造了萬級的樣本供crf模型訓練。

但是省略query的多樣性很高,使用萬級的樣本是不夠的,在線上模型無法快速應用深度模型的情況下,我們使用了boostraping的方式,藉助深度模型的泛化能力,離線構造了大量樣本。

使用了這種方式,樣本從萬級很容易擴充到百萬級,我們仍然使用crf模型進行訓練和線上應用。

在省略模塊,我們完成了規則到機器學習的升級,引入了成分以外的其他特徵,提升了模型的魯棒性。同時並且利用離線深度學習的方式進行樣本構造的循環,提升了樣本的多樣性,使得模型能夠更加接近crf的天花板。

在後續深度模型的建模中,我們逐步擺脫了對成分分析特徵的依賴,對query到命中poi核心直接進行建模,構建大量樣本,取得了進一步的收益。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

3c收購,鏡頭 收購有可能以全新價回收嗎?

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

賣IPHONE,iPhone回收,舊換新!教你怎麼賣才划算?