IM通訊協(xié)議專題學(xué)習(xí)(二):快速理解Protobuf的背景油狂、原理、使用寸癌、優(yōu)缺點

本文由vivo技術(shù)團(tuán)隊Li Guanyun分享专筷,為了提升閱讀體驗,進(jìn)行了較多修訂和重新排版蒸苇。

1仁堪、引言

Protobuf 作為一種跨平臺、語言無關(guān)填渠、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)通訊協(xié)議弦聂,已廣泛應(yīng)用于網(wǎng)絡(luò)數(shù)據(jù)交換的場景中(比如IM通信、分布式RPC調(diào)用等)氛什。

隨著互聯(lián)網(wǎng)的發(fā)展莺葫,分布式系統(tǒng)的異構(gòu)性會愈發(fā)突出,跨語言的需求會愈加明顯枪眉,同時?gRPC?也大有取代Restful之勢捺檬,而 Protobuf 作為gRPC 跨語言、高性能的法寶贸铜,我們技術(shù)人有必要深入理解 Protobuf 原理堡纬,為以后的技術(shù)更新和選型打下基礎(chǔ)。

借此機(jī)會蒿秦,我將個人的Protobuf學(xué)習(xí)過程以及實踐經(jīng)驗烤镐,總結(jié)成文,與大家一起探討學(xué)習(xí)棍鳖。本篇主要從Protobuf的基礎(chǔ)概念開始炮叶,包括技術(shù)背景碗旅、技術(shù)原理、使用方法和優(yōu)缺點镜悉。

PS:本篇本跟上篇《Protobuf從入門到精通祟辟,一篇就夠!》類似侣肄,都適合作為Protobuf的入門文章旧困,但本篇力求簡潔,盡量不涉及Protobuf的具體技術(shù)細(xì)節(jié)稼锅,目的是降低閱讀的門檻叮喳、提升閱讀效果,希望對你有用缰贝。

(本文已同步發(fā)布于:http://www.52im.net/thread-4081-1-1.html

2、系列文章

本文是系列文章中的第?2?篇畔濒,本系列總目錄如下:

IM通訊協(xié)議專題學(xué)習(xí)(一):Protobuf從入門到精通剩晴,一篇就夠!

IM通訊協(xié)議專題學(xué)習(xí)(二):快速理解Protobuf的背景侵状、原理赞弥、使用、優(yōu)缺點》(* 本文)

《IM通訊協(xié)議專題學(xué)習(xí)(三):由淺入深趣兄,從通信編解碼原理上理解Protobuf》(稍后發(fā)布..)

《IM通訊協(xié)議專題學(xué)習(xí)(四):從Base64到Protobuf绽左,詳解Protobuf的數(shù)據(jù)編碼原理》(稍后發(fā)布..)

《IM通訊協(xié)議專題學(xué)習(xí)(五):Protobuf到底比JSON快幾倍?請看全方位實測艇潭!》(稍后發(fā)布..)

《IM通訊協(xié)議專題學(xué)習(xí)(六):手把手教你如何在Android上從零使用Protobuf》(稍后發(fā)布..)

《IM通訊協(xié)議專題學(xué)習(xí)(七):手把手教你如何在NodeJS中從零使用Protobuf》(稍后發(fā)布..)

《IM通訊協(xié)議專題學(xué)習(xí)(八):金蝶隨手記團(tuán)隊的Protobuf應(yīng)用實踐(原理篇)? 》(稍后發(fā)布..)

《IM通訊協(xié)議專題學(xué)習(xí)(九):金蝶隨手記團(tuán)隊的Protobuf應(yīng)用實踐(實戰(zhàn)篇) 》(稍后發(fā)布..)

3拼窥、什么是Protobuf?

Protobuf(全稱是Protocol Buffers)是一種跨平臺蹋凝、語言無關(guān)鲁纠、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)的方法,可用于網(wǎng)絡(luò)通信數(shù)據(jù)交換及存儲鳍寂。

在序列化結(jié)構(gòu)化數(shù)據(jù)的機(jī)制中改含,Protobuf是靈活、高效迄汛、自動化的捍壤,相對常見的XML、JSON鞍爱,描述同樣的信息鹃觉,Protobuf序列化后數(shù)據(jù)量更小、序列化/反序列化速度更快睹逃、更簡單帜慢。

一旦定義了要處理的數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)之后,就可以利用Protobuf的代碼生成工具生成相關(guān)的代碼。只需使用 Protobuf 對數(shù)據(jù)結(jié)構(gòu)進(jìn)行一次描述粱玲,即可利用各種不同語言(proto3支持C++, Java, Python, Go, Ruby, Objective-C, C#)或從各種不同流中對你的結(jié)構(gòu)化數(shù)據(jù)輕松讀寫躬柬。

PS:類似的介紹,在上篇《Protobuf從入門到精通抽减,一篇就夠允青!》中也有涉及,有興趣可以一并閱讀之卵沉。

4颠锉、為什么是 Protobuf?

4.1 技術(shù)背景

大家可能會覺得 Google 發(fā)明 Protobuf 是為了解決序列化速度的史汗,其實真實的原因并不是這樣的琼掠。

Protobuf最先開始是 Google用來解決索引服務(wù)器?request/response?協(xié)議的。

在沒有Protobuf之前停撞,Google 已經(jīng)存在了一種?request/response?格式瓷蛙,用于手動處理?request/response?的編解碼。

這種sstk式也能支持多版本協(xié)議戈毒,不過代碼不夠優(yōu)雅:

if(protocolVersion=1) {

????doSomething();

} elseif(protocolVersion=2) {

????doOtherThing();

} ...

如果是非常明確的格式化協(xié)議艰猬,會使新協(xié)議變得非常復(fù)雜。因為開發(fā)人員必須確保請求發(fā)起者與處理請求的實際服務(wù)器之間的所有服務(wù)器都能理解新協(xié)議埋市,然后才能切換開關(guān)以開始使用新協(xié)議冠桃。

這也就是每個服務(wù)器開發(fā)人員都遇到過的低版本兼容、新舊協(xié)議兼容相關(guān)的問題道宅。

為了解決這些問題食听,于是Protobuf就誕生了。

4.2 Protobuf 誕生了

Protobuf 最初被寄予以下 2 個期望:

1)更容易引入新的字段污茵,并且不需要檢查數(shù)據(jù)的中間服務(wù)器可以簡單地解析并傳遞數(shù)據(jù)(而無需了解所有字段)碳蛋;

2)數(shù)據(jù)格式更加具有自我描述性,可以用各種語言來處理(比如C++, Java 等各種語言)省咨。

但這個版本的 Protobuf 仍需要自己手寫解析的代碼肃弟。

隨著Protobuf的發(fā)展、演進(jìn)零蓉,它具有了更多的特性:

1)自動生成的序列化和反序列化代碼(避免了手動解析的需要笤受。官方提供自動生成代碼工具,各個語言平臺的基本都有)敌蜂;

2)除了用于數(shù)據(jù)交換之外箩兽,Protobuf也被用作某些持久化數(shù)據(jù)的便捷自描述格式。

Protocol Buffers 命名的由來:

Why the name "Protocol Buffers"?

The name originates from the early days of the format, before we had the protocol buffer compiler to generate classes for us. At the time, there was a class called ProtocolBuffer which actually acted as a buffer for an individual method. Users would add tag/value pairs to this buffer individually by calling methods like AddValue(tag, value). The raw bytes were stored in a buffer which could then be written out once the message had been constructed.

Since that time, the "buffers" part of the name has lost its meaning, but it is still the name we use. Today, people usually use the term "protocol message" to refer to a message in an abstract sense, "protocol buffer" to refer to a serialized copy of a message, and "protocol message object" to refer to an in-memory object representing the parsed message.

4.3 Protobuf 在谷歌業(yè)務(wù)中的地位

Protobuf 現(xiàn)在是 Google 用于數(shù)據(jù)交換和存儲的通用語言章喉。

谷歌代碼樹中定義了 48162 種不同的消息類型汗贫,包括 12183 個?.proto?文件身坐。它們既用于 RPC 系統(tǒng),也用于在各種存儲系統(tǒng)中持久存儲數(shù)據(jù)落包。

Protobuf 誕生之初是為了解決服務(wù)器端新舊協(xié)議(高低版本)兼容性問題部蛇,名字也很體貼——“協(xié)議緩沖區(qū)”,只不過后期慢慢發(fā)展成用于傳輸數(shù)據(jù)咐蝇。

5涯鲁、Protobuf 協(xié)議的工作原理

如下圖所示:可以看到,對于序列化協(xié)議來說有序,使用方只需要關(guān)注業(yè)務(wù)對象本身抹腿,即 idl 定義,序列化和反序列化的代碼只需要通過工具生成即可旭寿。

6警绩、Protobuf 協(xié)議的消息定義

Protobuf 的消息是在idl文件(.proto)中描述的。

下面是本次樣例中使用到的消息描述符?customer.proto

syntax = "proto3";


package domain;


option java_package = "com.Protobuf.generated.domain";

option java_outer_classname = "CustomerProtos";


message Customers {

????repeated Customer customer = 1;

}


message Customer {

????int32 id= 1;

????string firstName = 2;

????string lastName = 3;


????enum EmailType {

????????PRIVATE = 0;

????????PROFESSIONAL = 1;

????}


????message EmailAddress {

????????string email = 1;

????????EmailType type= 2;

????}


????repeated EmailAddress email = 5;

}

上面的消息比較簡單盅称,Customers包含多個Customer(Customer包含一個id字段肩祥、一個firstName字段、一個lastName字段以及一個email的集合)微渠。

除了上述定義外,文件頂部還有三行可幫助代碼生成器的申明:

1)syntax = "proto3":用于idl語法版本咧擂,目前有兩個版本proto2和proto3逞盆,兩個版本語法不兼容,如果不指定松申,默認(rèn)語法是proto2(由于proto3比proto2支持的語言更多云芦,語法更簡潔,本文使用的是proto3)贸桶;

2)package domain:此配置用于嵌套生成的類/對象舅逸;

3)option java_package:生成器還使用此配置來嵌套生成的源(此處的區(qū)別在于這僅適用于Java,在使用Java創(chuàng)建代碼和使用JavaScript創(chuàng)建代碼時皇筛,使用了兩種配置來使生成器的行為有所不同琉历。也就是說,Java類是在包com.Protobuf.generated.domain下創(chuàng)建的水醋,而JavaScript對象是在包domain下創(chuàng)建的)旗笔。

Protobuf 提供了更多選項和數(shù)據(jù)類型,本文不做詳細(xì)介紹拄踪,感興趣可以參考官方文檔蝇恶。

7、Protobuf 的代碼生成

首先安裝 Protobuf 編譯器 protoc(點這里有詳細(xì)的安裝教程)惶桐。

安裝完成后撮弧,可以使用以下命令生成 Java 源代碼:

1protoc --java_out=./src/main/java./src/main/idl/customer.proto

上述命令的意圖是:從項目的根路徑執(zhí)行該命令潘懊,并添加了兩個參數(shù)?java_out(即定義?./src/main/java/?為Java代碼的輸出目錄;而?./src/main/idl/customer.proto?是.proto文件所在目錄)贿衍。

生成的代碼非常復(fù)雜授舟,但幸運(yùn)的是它的用法卻非常簡單:

CustomerProtos.Customer.EmailAddress email = CustomerProtos.Customer.EmailAddress.newBuilder()

????????.setType(CustomerProtos.Customer.EmailType.PROFESSIONAL)

????????.setEmail("crichardson@email.com").build();


CustomerProtos.Customer customer = CustomerProtos.Customer.newBuilder()

????????.setId(1)

????????.setFirstName("Lee")

????????.setLastName("Richardson")

????????.addEmail(email)

????????.build();

// 序列化

byte[] binaryInfo = customer.toByteArray();

System.out.println(bytes_String16(binaryInfo));

System.out.println(customer.toByteArray().length);

// 反序列化

CustomerProtos.Customer anotherCustomer = CustomerProtos.Customer.parseFrom(binaryInfo);

System.out.println(anotherCustomer.toString());

8、Protobuf 的性能數(shù)據(jù)

我們簡單地以上述Customers為模型舌厨,分別構(gòu)造岂却、選取小對象、普通對象裙椭、大對象進(jìn)行性能對比曹铃。

序列化耗時以及序列化后數(shù)據(jù)大小對比:

反序列化耗時:

更多性能數(shù)據(jù)可以參考官方的測試Benchmark

9未巫、Protobuf 的優(yōu)點

9.1效率高

從序列化后的數(shù)據(jù)體積角度约急,與XML、JSON這類文本協(xié)議相比炊汤,Protobuf通過?T-(L)-V(TAG-LENGTH-VALUE)方式編碼正驻,不需要", {, }, :等分隔符來結(jié)構(gòu)化信息。同時在編碼層面使用varint壓縮抢腐。

所以描述同樣的信息姑曙,Protobuf序列化后的體積要小很多,在網(wǎng)絡(luò)中傳輸消耗的網(wǎng)絡(luò)流量更少迈倍,進(jìn)而對于網(wǎng)絡(luò)資源緊張伤靠、性能要求非常高的場景。比如在移動網(wǎng)絡(luò)下的IM即時通訊應(yīng)用中啼染,Protobuf協(xié)議就是非常不錯的選擇(PS:這也是我為什么著手分享Protobuf系列文章的原因啦)宴合。

我們來簡單做個對比。

要描述如下JSON數(shù)據(jù):

1{"id":1,"firstName":"Chris","lastName":"Richardson","email":[{"type":"PROFESSIONAL","email":"crichardson@email.com"}]}

使用JSON序列化后的數(shù)據(jù)大小為118byte:

7b226964223a312c2266697273744e616d65223a224368726973222c226c6173744e616d65223a2252696368617264736f6e222c22656d61696c223a5b7b2274797065223a2250524f46455353494f4e414c222c22656d61696c223a226372696368617264736f6e40656d61696c2e636f6d227d5d7d

而使用Protobuf序列化后的數(shù)據(jù)大小為48byte:

0801120543687269731a0a52696368617264736f6e2a190a156372696368617264736f6e40656d61696c2e636f6d1001

從序列化/反序列化速度角度迹鹅,與XML卦洽、JSON相比,Protobuf序列化/反序列化的速度更快斜棚,比XML要快20-100倍阀蒂。

9.2支持跨平臺、多語言

Protobuf是平臺無關(guān)的弟蚀,無論是Android脂新、iOS、PC粗梭,還是C#與Java争便,都可以利用Protobuf進(jìn)行無障礙通訊。

proto3支持C++断医、Java滞乙、Python奏纪、Go、Ruby斩启、Objective-C序调、C#(詳見《Protobuf從入門到精通,一篇就夠》)兔簇。

9.3擴(kuò)展性发绢、兼容性好

Protobuf具有向后兼容的特性:更新數(shù)據(jù)結(jié)構(gòu)以后,老版本依舊可以兼容垄琐,這也是Protobuf誕生之初被寄予解決的問題边酒,因為編譯器對不識別的新增字段會跳過不處理。

9.4使用簡單

Protobuf 提供了一套編譯工具狸窘,可以自動生成序列化墩朦、反序列化的樣板代碼,這樣開發(fā)者只要關(guān)注業(yè)務(wù)數(shù)據(jù)idl翻擒,簡化了編碼解碼工作以及多語言交互的復(fù)雜度氓涣。

10、Protobuf 的缺點

Protobuf的優(yōu)點很突出陋气,但缺點也很明顯劳吠。

Protobuf的缺點主要是:

1)不具備自描述能力:跟XML、JSON相比巩趁,這兩者是自描述的痒玩,而ProtoBuf則不是;

2)數(shù)據(jù)可讀性非常差:ProtoBuf是二進(jìn)制協(xié)議晶渠,如果沒有idl文件凰荚,就無法理解二進(jìn)制數(shù)據(jù)流燃观,對調(diào)試非常不友好褒脯。

不過:Charles已經(jīng)支持Protobuf協(xié)議,導(dǎo)入數(shù)據(jù)的描述文件即可缆毁,詳情可參考?Charles Protocol Buffers番川。

然而:由于沒有idl文件無法解析二進(jìn)制數(shù)據(jù)流,ProtoBuf在一定程度上可以保護(hù)數(shù)據(jù)脊框,提升核心數(shù)據(jù)被破解的門檻颁督,降低核心數(shù)據(jù)被盜爬的風(fēng)險(也算是缺點變優(yōu)點的典型范例)。

11浇雹、參考資料

[1]?Protobuf官方網(wǎng)站

[2]?Protobuf從入門到精通沉御,一篇就夠!

[3]?如何選擇即時通訊應(yīng)用的數(shù)據(jù)傳輸格式

[4]?強(qiáng)列建議將Protobuf作為你的即時通訊應(yīng)用數(shù)據(jù)傳輸格式

[5]?APP與后臺通信數(shù)據(jù)格式的演進(jìn):從文本協(xié)議到二進(jìn)制協(xié)議

[6]?面試必考昭灵,史上最通俗大小端字節(jié)序詳解

[7]?移動端IM開發(fā)需要面對的技術(shù)問題(含通信協(xié)議選擇)

[8]?簡述移動端IM開發(fā)的那些坑:架構(gòu)設(shè)計吠裆、通信協(xié)議和客戶端

[9]?理論聯(lián)系實際:一套典型的IM通信協(xié)議設(shè)計詳解

[10]?58到家實時消息系統(tǒng)的協(xié)議設(shè)計等技術(shù)實踐分享

(本文已同步發(fā)布于:http://www.52im.net/thread-4081-1-1.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伐谈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子试疙,更是在濱河造成了極大的恐慌诵棵,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祝旷,死亡現(xiàn)場離奇詭異履澳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)怀跛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門距贷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人敌完,你說我怎么就攤上這事储耐。” “怎么了滨溉?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵什湘,是天一觀的道長。 經(jīng)常有香客問我晦攒,道長闽撤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任脯颜,我火速辦了婚禮哟旗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘栋操。我一直安慰自己闸餐,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布矾芙。 她就那樣靜靜地躺著舍沙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剔宪。 梳的紋絲不亂的頭發(fā)上拂铡,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音葱绒,去河邊找鬼感帅。 笑死,一個胖子當(dāng)著我的面吹牛地淀,可吹牛的內(nèi)容都是我干的失球。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼帮毁,長吁一口氣:“原來是場噩夢啊……” “哼实苞!你這毒婦竟也來了璧微?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤硬梁,失蹤者是張志新(化名)和其女友劉穎前硫,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荧止,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡屹电,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了跃巡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片危号。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖素邪,靈堂內(nèi)的尸體忽然破棺而出外莲,到底是詐尸還是另有隱情,我是刑警寧澤兔朦,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布偷线,位于F島的核電站,受9級特大地震影響沽甥,放射性物質(zhì)發(fā)生泄漏声邦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一摆舟、第九天 我趴在偏房一處隱蔽的房頂上張望亥曹。 院中可真熱鬧,春花似錦恨诱、人聲如沸媳瞪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛇受。三九已至,卻和暖如春硫豆,著一層夾襖步出監(jiān)牢的瞬間龙巨,已是汗流浹背笼呆。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工熊响, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诗赌。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓汗茄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親铭若。 傳聞我的和親對象是個殘疾皇子洪碳,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容