Protobuf协议格式详解
protobuf 是google開源的一個(gè)序列化框架,類似xml,json,最大的特點(diǎn)是基于二進(jìn)制,比傳統(tǒng)的XML表示同樣一段內(nèi)容要短小得多。還可以定義一些可選字段,用于服務(wù)端與客戶端通信。前面幾篇文章說了protobuf的用法,看到網(wǎng)上也沒有分析protobuf協(xié)議的文章,就利用一些時(shí)間寫了?protobuf 的協(xié)議分析,希望大家喜歡。
protobuf協(xié)議核心思想
基于128bits的數(shù)值存儲(chǔ)方式(Base 128 Varints)
數(shù)據(jù)表示方式:每塊數(shù)據(jù)由接連的若干個(gè)字節(jié)表示(小的數(shù)據(jù)用1個(gè)字節(jié)就可以表示),每個(gè)字節(jié)最高位標(biāo)識(shí)本塊數(shù)據(jù)是否結(jié)束(1:未結(jié)束,0:結(jié)束),低7位表示數(shù)據(jù)內(nèi)容。(可以看出數(shù)據(jù)封包后體積至少增大14.2%)
數(shù)字1的表示方法為:0000 0001,這個(gè)容易理解
數(shù)字300的表示方法為:1010 1100 0000 0010
protobuf字節(jié)序是小端字節(jié)序,所以這個(gè)數(shù)字實(shí)際是0000 0010 1010 1100
| 1010 1100 0000 0010→ 010 1100 ?000 0010 如下:000 0010 ?010 1100→ ?000 0010 ++ 010 1100→ ?10 0101100→ ?256 + 32 + 8 + 4 = 300 |
基于序號(hào)的協(xié)議字段映射(類似key-value結(jié)構(gòu))
所以字段可以亂序,可缺段(記optional)
| message person{? ? required string name ? ? ?= 1;? ? required string country ?= 2;? ? optional int32 age ? ? ? ? ? = 3;} |
效果相當(dāng)于json數(shù)據(jù):person?= [{1: "john"}, {2: ?"USA"}, {3: 30}],其中{3: 30} 還可以不傳,person還可以傳成 [{2: ?"USA"}, {1: "john"}],對(duì)端仍舊可以正常解析。
基于無符號(hào)數(shù)的帶符號(hào)數(shù)表示(ZigZag 編碼)
| 0 | 0 |
| -1 | 1 |
| 1 | 2 |
| -2 | 3 |
| 2147483647 | 4294967294 |
| -2147483648 | 4294967295 |
| sint32類型編碼如下:(n << 1) ^ (n >> 31) sint64類型編碼如下:(n << 1) ^ (n >> 63)? |
協(xié)議數(shù)據(jù)結(jié)構(gòu)
protobuf怎么在一長(zhǎng)串二進(jìn)制中表示若干個(gè)數(shù)據(jù)?
做法就是每塊數(shù)據(jù)前加一個(gè)數(shù)據(jù)頭,表示數(shù)據(jù)類型及協(xié)議字段序號(hào)。
msg1_head + msg1 + msg2_head + msg2 + ...
數(shù)據(jù)頭也是基于128bits的數(shù)值存儲(chǔ)方式,一般1個(gè)字節(jié)就可以表示:
| message Test1 {? ? required int32 a =?1;} 如上創(chuàng)建了 Test1 的結(jié)構(gòu)并且把 a 設(shè)為 2,序列化好的二進(jìn)制數(shù)據(jù)為: 0000 1000?0000 0010 |
以上數(shù)據(jù)轉(zhuǎn)成十六進(jìn)制也就是 08 02,其中 8 是怎么得到的?
| 000 1000 低3位表示數(shù)據(jù)類型:0,其他表示協(xié)議字段序號(hào):1,加上最高位0, 結(jié)果就是8 |
數(shù)據(jù)類型的表示如下:
| 0 | Varint | 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 |
寫在最后
protobuf的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)前面也提到了,主要有兩個(gè):
1、序列化和反序列化效率比 xml 和 json 都高(這個(gè)protobuf 自己做了測(cè)試,鏈接要翻墻);
2、字段可以亂序,欠缺,因此可以用來兼容舊的協(xié)議,或者是減少協(xié)議數(shù)據(jù)。
但是字段允許亂序欠缺,反過來也是缺點(diǎn)。所以這里總結(jié)?protobuf?兩個(gè)缺點(diǎn),一個(gè)跟這有關(guān):
1、如果字段過多,或者嵌套過深,都會(huì)影響反序列化效率,解析每一塊數(shù)據(jù)都要根據(jù)序號(hào)找到對(duì)應(yīng)的位置然后再插入到已解析好的數(shù)據(jù)中。
2、數(shù)據(jù)基于128bits的存儲(chǔ)方式,單塊數(shù)據(jù)比較大時(shí)效率很受影響。解析數(shù)據(jù)需要取到所有字節(jié)的低7位,然后再拼成一整塊數(shù)據(jù)。
以上兩個(gè)缺點(diǎn),特別是對(duì)于erlang這類沒有指針的語言來說,代價(jià)就相當(dāng)昂貴。
3、協(xié)議序號(hào)也要占空間,序號(hào)越大占空間越大,當(dāng)序號(hào)小于16時(shí)無需額外增加字節(jié)就可以表示。
protobuf 分析題
最后貼一道分析題,如果看得懂,基本就了解 protobuf 的協(xié)議內(nèi)容了| message Test2 {? ? required int32 a = 3;}創(chuàng)建了Test2結(jié)構(gòu),a 賦值 150, 結(jié)果是0001 1000 1001 0110 ?0000 0001這個(gè)數(shù)寫成十進(jìn)制就是 24 150 1,怎么得到這個(gè)數(shù)據(jù)? |
總結(jié)
以上是生活随笔為你收集整理的Protobuf协议格式详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云容器镜像加速器
- 下一篇: Java基础(数组)数组缩减