介绍
protobuf 是一种google发明的数据序列化机制。官网的解释是:
protocol buffers(简称protobuf)是google的语言中立、平台中立、可扩展的机制,用来对结构化数据进行序列化,类似于xml,但是更小、更快、更简单。只要定义好如何结构化你的数据,就可以使用生成好的代码取写和读取各种数据流的数据,支持各种语言。
Varint
protobuf定义的格式形如:
message Test1 {
optional int32 a = 1;
}
Varint的序列化和反序列化过程如下。
varint存在的意义:
当我们日常使用的数字都是很小的数字,并没有用完32bit位时,如果全都用32bit填0去存,会造成大量的存储浪费,varint变长整数的思想对于1、2、3这样的小数字能够缩小空间,但是如果经常使用的数字都是很大的整数,则返回会占用更多的空间。
protobuf - 键值对
protobuf的消息结构本质上一串KV键值对,序列化成二进制的时候key只存了字段的序号,至于字段的名字和类型都是解码端通过序号查proto文件获取到的。
因为protobuf的本质是一系列键值对,所以它的二进制结构形如下表,key是字段的序号+解析方式,value是一堆字符串。
| key | value |
|---|---|
| field_number + Varint | ac 02 |
| field_number + 64-bit | 12 34 56 78 22 34 56 78 |
| field_number + Length-delimited | 07 [ 74 65 73 74 69 6e 67 ] |
| field_number + Start group | (deprecated) |
| field_number + End group | (deprecated) |
| field_number + 32-bit | 12 34 56 78 |
protobuf的key,实际上的格式如下。
(field_number << 3) | wire_type
key - wire type
wire type,protobuf解析二进制信息时根据这个类型决定后面读多少字节,字段类型与wire type的映射关系如下表。
| Type | Meaning | Used For |
|---|---|---|
| 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 |
Length-delimited
Signed Integers(有符号的整数)
protobuf对于有符号整数的解析也是通过varint,通过varint确定解析多少个字节,但是如果将某个负数(如-1)直接当做32位无符号整数去序列化,那么对于-1这种负数来说,由于最高位符号位的存在,导致varint结果会很大,远超-1包含的信息量。因此protobuf采用了ZigZag编码方式对varint的结果进行编码,进一步压缩数据,过程如下。
Optional And Repeated Elements
message Test4 {
repeated int32 d = 4
}
message Test4 {
repeated int32 d = 4 [packed=true];
}
标识packed=true时,和上面不同的是,这种时候是把d用Length-delimited的方法序列化的,且只支持原始数字类型(varint、32-bit、64-bit)
其它
protobuf序列化后的字段顺序是不固定的,官方文档中列出了各种可能性,总之字段顺序是不固定的,注意两次序列化的结果,它们hash、CRC、FingerPrint可能完全不同。