Protocol Buffer是Google出品的數據傳輸協議,目前已經普遍用於客戶端和服務器之間的數據交互,清晰理解Protocol Buffer原理頗有必要,本文主要解密Protocol Buffer爲何更小,更快,不瞭解Protocol Buffer的能夠看下以前對Protocol Buffer的介紹java
Protocol Buffer更快,更小的主要緣由以下:算法
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
複製代碼
上述數據在序列化時,query,page_number以及result_per_page的key不會參與,由編號1,2,3替代,這樣在反序列的時候能夠直接經過編號找到對應的key,這樣作確實能夠減少傳輸數據,可是編號一旦肯定就不可更改bash
Protocol Buffer的數據組成方式爲TLV,數據結構圖以下: 服務器
Protocol Buffer定義了以下的數據類型,其中部分數據類型已經再也不使用:數據結構
類型 | 釋義 | 備註 |
---|---|---|
0 | 可變長度編碼 | int32 int64 uint32 uint64 sint32 sint64 bool enum |
1 | 64位長度 | fixed64 sfixed64 double |
2 | value 的長度 | string bytes message packed repeated fiels |
3 | Start Group | 廢棄 |
4 | End Group | 廢棄 |
5 | 32位長度 | fixed32 sfixed32 float |
上面已經介紹了Protocol Buffer的數據結構及Tag的類型,可是Tag塊並非只表示數據類型,其中數據編號也在Tag塊中,Tag的生成規則以下:工具
(field_number << 3) | wire_type
複製代碼
其中Tag塊的後3位表示數據類型,其餘位表示數據編號post
Java中整數類型的長度都是肯定的,如int類型的長度爲4個字節,可表示的整數範圍爲-2^31——2^31-1,可是實際開發中用到的數字均比較小,會形成字節浪費,可變長度編碼就能很好的解決這個問題,可變長度編碼規則以下:ui
舉個例子: 編碼
10000001 00000011 ——> 00000110000001 表示的10進制數爲:2^0 + 2^7 + 2^8 = 385
經過上面的例子能夠知道一個字節表示的數的範圍0-128,上面介紹的Tag生成算法中因爲後3位表示數據類型,因此Tag中1-15編號只佔用1個字節,因此確保編號中1-15爲經常使用的,減小數據大小spa
可變長度編碼惟一的缺點就是當數很大的時候int32須要佔用5個字節,可是從統計學角度來講,通常不會有這麼大的數
Java中最高位表示整數的正負,經過上面可變長度編碼介紹,最高位被用來做爲數據結束標識符了,因此無法經過最高位來表示數據的正負,使用int32或者int64表示負數的時候佔用10個字節,這是Protocol Buffer源碼中規定的,因此若是要使用負數強烈不建議使用int32和int64,建議使用sint32和sint64,sint32和sint64先使用zigZag編碼,生成的數再使用可變長度編碼,下面介紹一下zigzag編碼.
zigzag編碼的代碼以下:
Zigzag(n) = (n << 1) ^ (n >> 31), n 爲 sint32 時
Zigzag(n) = (n << 1) ^ (n >> 63), n 爲 sint64 時
複製代碼
按照這種編碼方式,對應的數字以下:
定長編碼其實沒什麼說的,double float等數據結構的長度是肯定的,當解析到這種類型的數據時,直接取對應長度的數據便可
上面介紹了Protocol Buffer的原理,如今經過實例來展現分析過程,咱們定義的proto文件以下:
message Person {
string name = 1;
int32 id = 2;
}
複製代碼
經過Protocol Buffer提供的工具,建立對應的源文件而且設置對應的值:name=test id=1,序列化後的字節以下:
上面介紹了Protocol Buffer的原理,解釋了爲何Protocol Buffer更快,更小,這裏再總結一下: