日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Protocol Buffers的应用与分析

發(fā)布時(shí)間:2023/12/2 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Protocol Buffers的应用与分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Protocol Buffers的應(yīng)用與分析 明塵

1? Protocol Buffers的介紹

Protocol Buffers是一種用于序列化結(jié)構(gòu)化數(shù)據(jù)的機(jī)制,它具有靈活、高效、自動(dòng)化的特點(diǎn)。類似于XML,但是比XML更小巧、快捷、簡(jiǎn)單。在Google?幾乎所有它內(nèi)部的RPC協(xié)議和文件格式都是采用PB。
PB具有以下特點(diǎn):

  • 平臺(tái)無關(guān)、語言無關(guān)
  • 高性能 比XML塊20-100倍
  • 體積小 比XML小3-10倍
  • 使用簡(jiǎn)單
  • 兼容性好
  • 在這里,我做了個(gè)小實(shí)驗(yàn),將一個(gè)29230KB的自定義格式的文本數(shù)據(jù)轉(zhuǎn)換成PB和XML:

    ?PBXML
    轉(zhuǎn)換后的大小21011KB43202KB
    解析時(shí)間(100次循環(huán))18610ms169251ms
    完成解析所寫代碼行數(shù)1行50行

    與官方說法的差距,主要可能是因?yàn)閼?yīng)用場(chǎng)景不同,我的測(cè)試數(shù)據(jù)中字段比較長(zhǎng)

    表1:PB與XML的實(shí)驗(yàn)比較

    可見,PB作為一種輕量級(jí)的數(shù)據(jù)協(xié)議,在時(shí)間、空間上都有一定的優(yōu)勢(shì)。

    2? Protocol Buffers的簡(jiǎn)單應(yīng)用

    2.1? 創(chuàng)建流程

    2.1.1? 定義一個(gè).proto文件

    新建一個(gè)文件,命名為addressbook.proto,內(nèi)容如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package tutorial;//命名空間 option java_package = "com.example.tutorial";//生成文件的包名 option java_outer_classname = "AddressBookProtos";//類名 message Person { //要描述的結(jié)構(gòu)化數(shù)據(jù) ????required string name = 1;//required表示這個(gè)字段不能為空 ????required int32 id = 2;//等號(hào)后面的內(nèi)容為數(shù)字別名 ????optional string email = 3;//optional表示可以為空 ????PhoneNumber {//內(nèi)部message ????????required string number = 1; ????????optional int32 type = 2; ????} ????repeated PhoneNumber phone = 4 } message AddressBook { ????repeated Person person = 1;//是個(gè)集合 }

    對(duì)以上內(nèi)容的一點(diǎn)解釋:

    • PB所支持的元類型數(shù)據(jù)請(qǐng)參考:PB元類型數(shù)據(jù)
    • 修飾符required:這個(gè)修飾符應(yīng)該謹(jǐn)慎使用,濫用會(huì)導(dǎo)致后續(xù)的修改容易出現(xiàn)兼容性問題;
    • 修飾符optional:對(duì)于常出現(xiàn)的屬性,為節(jié)省空間應(yīng)該取1-16的別名;
    • PB是以key-value的形式來將結(jié)構(gòu)化數(shù)據(jù)序列化的。它采用了將等號(hào)后的數(shù)字別名以及屬性的類型用varints編碼成一個(gè)數(shù)字,來作為key。

    2.1.2? 使用PB編譯器

    輸入:protoc????? -I=$SRC_DIR –java_out=$DST_DIR $SRC_DIR/addressbook.proto
    其中??? -I指定.proto文件所在目錄
    –java_out指定生成java文件所在的目錄

    2.1.3? 使用PB的API來寫入和讀取messages

    經(jīng)過以上步驟,會(huì)在指定的$DST_DIR目錄下生成一個(gè)AddressBookProtos.java的類。在maven中引入protobuf-java這個(gè)依賴后,利用這個(gè)類,便能序列化/反序列化數(shù)據(jù)了。
    生成的代碼結(jié)構(gòu)如下:

    1 2 3 4 5 6 7 class AddressBookProtos{ ????class Person{ ????????class PhoneNumber{class Builder{} } ????????class Builder{} ????} ????class AddressBook{class Builder{} } }

    可以看到Person、PhoneNumber、AddressBook這些內(nèi)部類則對(duì)應(yīng)了所定義的那些message。

    2.2? 序列化數(shù)據(jù)及分析

    通過閱讀代碼可以看到,以上三個(gè)類的成員變量都是private類型的,并且,只提供了getter方法,而沒有提供setter方法去為數(shù)據(jù)變量賦值。
    PB利用了內(nèi)部類可以訪問到外部類中私有成員變量的特性。對(duì)外部類的任何賦值操作都需要通過Builder內(nèi)部類來進(jìn)行。Builder中有一個(gè)指向外部類的引用(名為result),當(dāng)賦值完成,調(diào)用Builder的build()方法時(shí),會(huì)把這個(gè)對(duì)象返回,同時(shí)使result指向null。
    PB通過這樣一種方式保證了數(shù)據(jù)安全性,一旦數(shù)據(jù)構(gòu)建完畢,將無法再對(duì)其進(jìn)行修改。
    拿PhoneNumber這個(gè)類來說,對(duì)成員變量number、type賦值,需要以如下方式來進(jìn)行:

    1 2 3 4 5 6 7 PhoneNumber.Builder builder = PhoneNumber.newBuilder(); //調(diào)用setter賦值,setter返回了this,所以可以鏈?zhǔn)奖硎?builder.setNumber("111").setType(1); //賦值完成后,調(diào)用Builder的build方法,將返回PhoneNumber對(duì)象 PhoneNumber phoneNumber = builder.build();

    構(gòu)建完成后,可以調(diào)用writeTo方法,將數(shù)據(jù)寫入數(shù)據(jù)流中。

    2.3? 反序列化及分析

    一行代碼便能完成反序列化:

    1 AddressBook ?list = AddressBook .parseFrom(inputStream或buffer);

    背后PB做了很多事情:

  • 根據(jù)inputStream或者buffer去構(gòu)造一個(gè)CodedInputStream;
  • 然后使用生成代碼中的mergeFrom方法,去解析二進(jìn)制數(shù)據(jù):
    首先調(diào)用CodedInputStream的readTag,也就是從中取得key值(int類型),然后通過swtich塊來往對(duì)象中賦值(PB采用了Base 128 Varints的方式來編碼這個(gè)數(shù)字,后面會(huì)介紹這種方式的)。
  • 將數(shù)據(jù)解析完成后,會(huì)調(diào)用build()方法,將構(gòu)建好的對(duì)象返回。
  • 3? message的編碼特點(diǎn)

    PB之所以解析速度快、所占體積小,很大程度上是由它序列化的編碼特點(diǎn)來決定的。

    3.1 Base 128 Varints

    PB采用了Base 128 Varints來變長(zhǎng)編碼整數(shù):

  • 變長(zhǎng)編碼的整數(shù),它可能包含多個(gè)byte,對(duì)于每個(gè)byte的8位,其中后7位表示數(shù)值,最高的一位表示是否還有還有另一個(gè)byte,0表示沒有,1表示有;
  • 越前面的byte表示數(shù)值的低位,越后面的byte表示數(shù)值的高位;
  • 例子:
    300 ??varints? 編碼為:1010 1100 0000 0010
    解釋如下:
    300的2進(jìn)制編碼為:0001 0010 1100
    按照剛才的規(guī)則,高低位顛倒,截取最后的7為放在第一個(gè)byte,則第一byte為1010 1100(其中最高位1表示,后續(xù)還有byte);接著剩下的內(nèi)容放到第二個(gè)byte,為0000 0010(其中最高位0表示,后續(xù)無byte,這個(gè)數(shù)到這里截止了)。
    于是,合在一起為 1010 1100 0000 0010;

    3.2 Key-Value

    如前所述,PB的message是一系列的key-value對(duì),在二進(jìn)制數(shù)據(jù)中,使用varints數(shù)字(包含了別名以及屬性類型信息)來作為key,進(jìn)而通過由PB編譯器生成的代碼來構(gòu)造以及解析數(shù)據(jù)。
    PB將 key編碼成下面的結(jié)構(gòu):
    X YYYY ZZZ
    其中:最高位X表示是否還有后續(xù)的byte來編碼數(shù)字別名;YYYY用于編碼別名,定義了多余16個(gè)屬性,則需要用到額外的byte,所以出現(xiàn)頻率高的字段應(yīng)當(dāng)取1-16的別名);ZZZ表示這個(gè)字段的類型,PB支持的屬性的對(duì)應(yīng)規(guī)則如下表:

    TypeMeaningUsed For
    0Varintint32, int64, uint32, uint64, sint32,sint64, bool, enum
    164-bitfixed64, sfixed64, double
    2Length-delimitedstring, bytes, embedded messages,packed repeated fields
    3Start groupgroups (deprecated)
    4End groupgroups (deprecated)
    532-bitfixed32, sfixed32, floa

    表2:PB 屬性對(duì)應(yīng)規(guī)則
    例子:
    required int32 a=1;? 在應(yīng)用中給a賦值150?? ,序列化后08 96 01

    • 08代表的是key 0 0001 000, 最高位為0,表示這個(gè)key為一個(gè)byte,中間四位表示a的數(shù)字別名,最后三位表示a的屬性類型;
    • 96 01代表的是value,二進(jìn)制為:1001 0110 0000 0001
      → 001 0110??? 000 0001(去掉最高位)
      → 22????????????? +? 1*2^7 = 150

    3.3 Zig-Zag

    采用varints的方式編碼有符號(hào)的整數(shù),效率比較差,因?yàn)樨?fù)數(shù)的最高位是1,這樣就導(dǎo)致了情況類似于編碼一個(gè)很大的數(shù)。

    為了解決這個(gè)問題,Protocol Buffers定義了sint32/sint64屬性,他們采用了“之字形”(ZigZag)編碼的方式,將負(fù)數(shù)編碼成正數(shù),交替進(jìn)行。看了下表就很好理解了:

    Signed OriginalEncoded As
    00
    -11
    12
    -23
    21474836474294967294
    21474836484294967295

    表3:Zig-Zag編碼規(guī)則
    利用這個(gè)方式,可以有效地節(jié)省存儲(chǔ)空間,也能提高解析效率。

    了解了以上內(nèi)容,對(duì)于其他數(shù)據(jù)類型的編碼,也是很好理解的,大家可以參考官方文檔,這里不做詳述。

    4 其他

    官方文檔中,有提到PB提供了RPC的接口,但是沒有提供具體實(shí)現(xiàn)。當(dāng)在的.proto文件中,加入如下定義:

    1 2 3 service XXX { ????rpc MMM(request) returns(response); }

    PB便會(huì)為你生成一個(gè)代表這個(gè)服務(wù)的XXX虛類,通過實(shí)現(xiàn)這個(gè)類中的abstract MMM方法,以及提供RpcChannel的實(shí)現(xiàn),你便可以利用Protocol Buffers實(shí)現(xiàn)你的RPC了。

    第三方的RPC實(shí)現(xiàn)大家可以參考ThirdPartyRPC

    在這里,我利用了第三方實(shí)現(xiàn)protobuf-socket-rpc,寫了一個(gè)小例子,有興趣的可以看看。如下:Protocol buffer的rpc例子

    5 小結(jié)

    PB具有跨平臺(tái)、解析速度快、序列化數(shù)據(jù)體積小、擴(kuò)展性高、使用簡(jiǎn)單的特點(diǎn)。但是我們也可以看到,相比于XML,PB的數(shù)據(jù),并不是自然可讀的;同時(shí)它生成的代碼不是純pojo,對(duì)于代碼有一定的侵入性。在你的項(xiàng)目中,如果對(duì)于以上缺點(diǎn)要求并不高,可以嘗試著使用PB。


    總結(jié)

    以上是生活随笔為你收集整理的Protocol Buffers的应用与分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。