Protocol Buffers 數(shù)據(jù)解析

突然間,好像對(duì)ProtoBuf有點(diǎn)陌生的感覺(jué),特此復(fù)習(xí)一下八回。

背景知識(shí)

Protocol Buffers可以理解為更快、更簡(jiǎn)單驾诈、更小的JSON或者XML缠诅,區(qū)別在于Protocol Buffers是二進(jìn)制格式,而JSON和XML是文本格式乍迄。

Protobuf經(jīng)序列化后以二進(jìn)制數(shù)據(jù)流形式存儲(chǔ)管引,這個(gè)數(shù)據(jù)流是一系列key-Value對(duì)。Key用來(lái)標(biāo)識(shí)具體的Field闯两,在解包的時(shí)候汉匙,Protobuf根據(jù) Key 就可以知道相應(yīng)的 Value 應(yīng)該對(duì)應(yīng)于消息中的哪一個(gè) Field。

Key 的定義如下:

(field_number << 3) | wire_type

Key由兩部分組成生蚁。第一部分是 field_number噩翠,比如消息 tutorial .Person中 field name 的 field_number 為 1。第二部分為 wire_type邦投。表示 Value 的傳輸類(lèi)型伤锚。Wire Type 可能的類(lèi)型如下表所示:

Type Meaning Used For
0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 64-bit fixed64, sfixed64, double
2 Length-delimi string, bytes, embedded messages, packed repeated fields
3 Start group Groups (deprecated)
4 End group Groups (deprecated)
5 32-bit fixed32, sfixed32, float

Protobuf的二進(jìn)制使用Varint編碼。Varint 是一種緊湊的表示數(shù)字的方法志衣。它用一個(gè)或多個(gè)字節(jié)來(lái)表示一個(gè)數(shù)字屯援,值越小的數(shù)字使用越少的字節(jié)數(shù)。這能減少用來(lái)表示數(shù)字的字節(jié)數(shù)念脯。

Varint 中的每個(gè) byte 的最高位 bit 有特殊的含義狞洋,如果該位為 1,表示后續(xù)的 byte 也是該數(shù)字的一部分绿店,如果該位為 0吉懊,則結(jié)束庐橙。其他的 7 個(gè) bit 都用來(lái)表示數(shù)字。因此小于 128 的數(shù)字都可以用一個(gè) byte 表示借嗽。大于 128 的數(shù)字态鳖,比如下面demo中的2000 就是用兩個(gè)字節(jié)來(lái)表示。

Demo Code

Demo Code 主要對(duì)比了一下恶导,用Gzip 壓縮序列化的Json數(shù)據(jù)與Protobuf 數(shù)據(jù)的速度和大小對(duì)比浆竭。可以看到惨寿,Protobuf 最大的一個(gè)優(yōu)點(diǎn)果然是在數(shù)據(jù)壓縮方面邦泄。那么數(shù)據(jù)是如何存取的呢?

手工解析pb數(shù)據(jù)

下面是一段Json數(shù)據(jù):

   NSDictionary *userInfoDic = @{@"userName":@"vedon",
                                @"age":@(2000),
                                @"mobileNumber":@"1501849235x",
                                @"address":@"廣州市平云廣場(chǎng)",
                                @"numberOfFriends":@(2000),
                                 };

它對(duì)應(yīng)的的pb 數(shù)據(jù)為:
<0a057665 646f6e10 1b1a0b31 35303138 34393233 35782215 e5b9bfe5 b79ee5b8 82e5b9b3 e4ba91e5 b9bfe59c ba2802>

解析userName

Key :
0a = 0000 1010
=> 0001 010
=> field_num = 0001 ,type = 010
=> field_num = 1,type = 2

Value:
05 = 0000 0101
=> 000 0101 (去掉最高位)
=> 5

讀取5個(gè)字符:
userName = 76 65 64 6f 6e => v e d o n

解析age

Key :
10 = 0001 0000
=> 001 0000 (去掉最高位)
=> field_num = 0010 ,type = 000
=> field_num = 2 ,type = 0

Value
1b = 0001 1011
=> 27
age = 27 .

如果age = 2000 ,2000超過(guò)一個(gè)字節(jié)表示的大小了裂垦,它會(huì)怎么表示呢顺囊?
改一下代碼,把年齡改成2000 缸废,發(fā)現(xiàn)除了key是不變外,value 從1b變成d00f

d00f = 1101 0000 | 0000 1111
          => 101 0000 | 000 1111 (little-endian)
          => 0111 1101 0000 = 2000
age = 2000

解析mobileNumber

Key:
1a = 0001 1010
=> 001 1010 (去掉最高位)
=> field_num = 0011 ,type = 010
=> field_num = 3 ,type = 2

Value:
0b = 0000 1011
=> 11

讀取11個(gè)字符:
31 35 30 31 38 34 39 32 33 35 78 => 1 5 0 1 8 4 9 2 3 5 x

優(yōu)點(diǎn)

  • 序列化速度快
  • 體積小
  • 兼容性好
  • Protocol Buffers的編譯器驶社,可以生成更容易在編程中使用的數(shù)據(jù)訪問(wèn)代碼

缺點(diǎn)

  • 缺乏自描述企量,可讀性差
  • 適用于內(nèi)部服務(wù)和存儲(chǔ)

JSON and Protobuf

Protobuf 相比 JSON 的優(yōu)勢(shì)在于它本身帶有嚴(yán)格的 schema validation,一份 .proto 文件既作為 input/output 的入口亡电,又作為規(guī)定數(shù)據(jù)格式的文檔届巩。例如:后端給一份.proto 文件,通過(guò)官方提供的工具份乒,生成對(duì)應(yīng)的oc 類(lèi)恕汇,在跨語(yǔ)言多人協(xié)作的項(xiàng)目上用起來(lái)不能更爽。

Protobuf 是 schema + data format或辖,schema 是我們可以看見(jiàn)的 proto 文件里的定義瘾英,data format 是它序列化成二進(jìn)制數(shù)據(jù)的格式。

最終選擇的時(shí)候還有個(gè)很關(guān)鍵的因素颂暇,就是公司整個(gè)大環(huán)境的技術(shù)選型缺谴,如果你所依賴(lài)的其他組件用 JSON 的較多,那么也不要刻意用 Protobuf 耳鸯。如果一個(gè)大型項(xiàng)目在一開(kāi)始就用了 Protobuf 作為數(shù)據(jù)格式的約定湿蛔,后期在數(shù)據(jù)一致性上可能出現(xiàn)很多麻煩就可以避免掉呢。

詳細(xì)的性能分析县爬,網(wǎng)上已經(jīng)有很詳細(xì)的了阳啥。這里推薦一篇文章:Beating JSON performance with Protobuf

擴(kuò)展閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市财喳,隨后出現(xiàn)的幾起案子察迟,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卷拘,死亡現(xiàn)場(chǎng)離奇詭異喊废,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)栗弟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)污筷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人乍赫,你說(shuō)我怎么就攤上這事瓣蛀。” “怎么了雷厂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵惋增,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我改鲫,道長(zhǎng)诈皿,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任像棘,我火速辦了婚禮稽亏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缕题。我一直安慰自己截歉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布烟零。 她就那樣靜靜地躺著瘪松,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锨阿。 梳的紋絲不亂的頭發(fā)上宵睦,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音墅诡,去河邊找鬼状飞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛书斜,可吹牛的內(nèi)容都是我干的诬辈。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼荐吉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼焙糟!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起样屠,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤穿撮,失蹤者是張志新(化名)和其女友劉穎缺脉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體悦穿,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡攻礼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了栗柒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片礁扮。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瞬沦,靈堂內(nèi)的尸體忽然破棺而出太伊,到底是詐尸還是另有隱情,我是刑警寧澤逛钻,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布僚焦,位于F島的核電站,受9級(jí)特大地震影響曙痘,放射性物質(zhì)發(fā)生泄漏芳悲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一边坤、第九天 我趴在偏房一處隱蔽的房頂上張望名扛。 院中可真熱鬧,春花似錦惩嘉、人聲如沸罢洲。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至殿较,卻和暖如春耸峭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背淋纲。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工劳闹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人洽瞬。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓本涕,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親伙窃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子菩颖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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