Protobuf是什么
- Protobuf是一种平台无关、语言无关、可扩展且轻便高效的序列化数据结构的协议,可以用于通信协议和数据存储等。
- 传输协议 - 如json、XML
- IDL - 接口描述语言
- 存储格式 - 序列化压缩后存储到数据库
- 核心竞争力
- 向前向后兼容性 - 新老版本兼容,无需考虑版本升级
- 多语言代码生成 - 支持Java、Python、PHP、Go等编程语言
- 快&小 - 序列化、反序列化速度快,压缩比优秀
关键技术
varints编码
- 每个字节使用其中7位保存数字,最高位表示后面是否还有内容
- 低位在前,高位在后
- 保留了
fixed32
和fixed64
,用于传递大的正数 int32
、int64
、unit32
、uint64
、bool
,序列化结果相互兼容,可以修改
zigzag编码
- 传统上,负数最高位为1,小负数会浪费编码长度
(n<<1)^(n>>31)
- -1将会被编码成1,1将会编码成2,-2将会被编码成3
sint32
和sint64
使用zigzag编码
message structure编码
- Tag-Value编码
Tag=(field_number<<3)|wire_type->varints
- wire_type:0表示varints,1表示固定64位,5表示固定32位
- wire_type:2表示Tag-Length-Value编码(TLV),Length使用varints
- string、bytes、message嵌套,都采用TLV编码
wire_type只有0、1、2、5,那么3和4去哪了?—被废除了
repeated编码
第一种方式:重复出现的相同tag
第二种方式:
(packed=true)
,TLVVV…编码仅有数字类型才可以使用第二种方法,protocol buffers 3(pb3)中默认第二种,pb2中需要指定,第一种任何情况下会被支持
非
repeated
情况出现重复tag,后面的覆盖前面的,因此optional
和repeated
相互兼容
Map编码
map<key_type,value_type>map_field=N
序列化结果完全等价于:
message MapFieldEntry {
key_type key = 1;
value_type value = 2;
}
repeated MapFieldEntry map_field = N;
protoc编译器
- C++编写的proto文件编译器
- 支持各种语言编写的插件,使用进程间通信传递信息
- Android和iOS上有对应的插件支持,自动调用protoc
Github地址:https://github.com/protocolbuffers/protobuf
如何使用
- 命名:message用驼峰,字段用下划线,enum用大写下划线,服务名方法名均为驼峰
- 无历史包袱,使用proto3,proto2也尽量不要使用
required
(pb3中被废除) - 不要修改旧字段,不要重复使用tag值
- 为最常用的字段保留1-15序号
- 与json转换,使用
pbjson
库
横向对比
- JSON:自解释,易读
- Thrift:自带rpc方案,跨平台好
- MessagePack:可以没有IDL,比JSON快和小
- Apache Avro:性能好,hadoop生态中成熟
- FlatBuffers:无需反序列化