Protocol Buffers 的技術(shù)優(yōu)勢詳述

前言

APP由于其移動特性,網(wǎng)絡(luò)通訊要求數(shù)據(jù)結(jié)構(gòu)更小、更快、更安全,主流的數(shù)據(jù)協(xié)議逐漸從文本協(xié)議遷移到序列化協(xié)議:

A(XML/JSON)=>B(Protocol Buffers/Hessian/...)

Protocol Buffers(以下簡稱PB)是google公司開放的,編程語言無關(guān),平臺無關(guān),可擴(kuò)展的序列化數(shù)據(jù)協(xié)議。你只需要用簡單的腳本語言,定義好數(shù)據(jù)結(jié)構(gòu),就可以生成所支持的編程語言的代碼,而PB幾乎支持所有的主流編程語言,包括Java、python、Objective-C,C++,Go, JavaNano, Ruby, and C#等。這些特性使得PB逐漸稱為序列化數(shù)據(jù)協(xié)議的首選。

這時候,你肯定心里在想,吹的很牛,牛在哪里實(shí)際上都沒說清楚!別急,下面會一一道來。

看兩個使用例子:

Person john = Person.newBuilder()
    .setId(1234)
    .setName("John Doe")
    .setEmail("jdoe@example.com")
    .build();
output = new FileOutputStream(args[0]);
john.writeTo(output);
Person john;
fstream input(argv[1],
    ios::in | ios::binary);
john.ParseFromIstream(&input);
id = john.id();
name = john.name();
email = john.email();

可以看出來,PB是通過二進(jìn)制流的方式來編碼解碼的,它的優(yōu)勢也是在于它的編碼技術(shù),所以下面重點(diǎn)講述proto3的序列化編碼和關(guān)鍵技術(shù)點(diǎn)。

序列化編碼

序列化編碼描述如何把PB消息轉(zhuǎn)成二進(jìn)制流,雖然在使用過程中不需要了解這個,但是了解它能讓你清楚地知道不同的消息類型,編碼后的字節(jié)數(shù)。

消息結(jié)構(gòu)

每一個消息都是用 key-value的結(jié)構(gòu)形式來存儲,其中key是正整型(varint編碼),表達(dá)為(field_number << 3) | wire_type,wire_type指定了每個數(shù)據(jù)類型的編碼方式,field_number是PB文件中消息序號,這種結(jié)構(gòu)決定了PB數(shù)據(jù)包會小于XML/JSON格式。

以下是wire_type結(jié)構(gòu)表:

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

Base 128 Varints

Base 128 varints是一種使用可變子節(jié)序列化整型數(shù)值的技術(shù),更小的數(shù)值使用更少的字節(jié)數(shù)。

Varints機(jī)制下,一個整型,除了最后一個字節(jié),其他子節(jié)都有最高有效位(MSB),用來指定后面的字節(jié)是否屬于該整型,也就是說只要MSB=1,那么后面的一個子節(jié)就是屬于該整型的。同時,要注意,varints的整型使用的是小端編碼。

例如:1用一個子節(jié)就可以表示,所以MSB位不用設(shè)置,所以編碼為

0000 0001

但是300,就編碼為

1010 1100 0000 0010

為什么呢?我們按照Varints機(jī)制解碼,得到最終結(jié)果

1010 1100 0000 0010
→ 010 1100  000 0010 #去掉MSB
→  000 0010 ++ 010 1100 # 小端還原
→  100101100 #拼接
→  256 + 32 + 8 + 4 = 300 #計算

然而,負(fù)數(shù)的最高位為1,根據(jù)上面的機(jī)制,該怎么表示?
Varints簡單暴力地把所有的負(fù)數(shù),無論是int32還是int64,都有10個字節(jié)來表示,這顯然是很挫的表示,所以varints只適合用來表示正整型,那么負(fù)整型怎么辦?

ZigZag

PB引入了ZigZag編碼來解決負(fù)整型編碼問題,ZigZag會把所有的負(fù)數(shù)映射成正數(shù),再使用varint編碼,達(dá)到壓縮目的。使用ZipZag編碼需要聲明類型為sint32或sint64。

編碼示例如下:

Signed Original Encoded As
0 0
-1 1
1 2
-2 3
2147483647 4294967294
-2147483648 4294967295

編碼公式如下,對于 sint32

(n << 1) ^ (n >> 31)

對于sint64

(n << 1) ^ (n >> 63)

>>表示為算術(shù)右移,即如果是正數(shù),左邊全部補(bǔ)0,如果是負(fù)數(shù),左邊全部補(bǔ)1。

Length-delimited

Length-delimited即加個數(shù)據(jù)頭,指定數(shù)據(jù)的長度,整體格式如下:

key length value

例如,字符串 "testing",UTF8編碼為

12 07 74 65 73 74 69 6e 67

0x12表示tag=2,type=2,0x07表示后面的數(shù)據(jù)長度為7個子節(jié)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容