Protobuf 是什么

  • Protobuf 是一种平台无关、语言无关、可扩展且轻便高效的序列化数据结构的协议,可以用于通信协议数据存储等
    • 传输协议 - 如 json、XML
    • IDL - 接口描述语言
    • 存储格式 - 序列化压缩后存储到数据库
  • 核心竞争力
    • 向前向后兼容性 - 新老版本兼容,无需考虑版本升级
    • 多语言代码生成 - 支持 Java、Python、PHP、Go 等编程语言
    • 快&小 - 序列化、反序列化速度快,压缩比优秀

关键技术

varints 编码

  1. 每个字节使用其中 7 位保存数字,最高位表示后面是否还有内容
  2. 低位在前,高位在后
  3. 保留了fixed32fixed64,用于传递大的正数
  4. int32int64unit32uint64bool,序列化结果相互兼容,可以修改

zigzag 编码

  1. 传统上,负数最高位为 1,小负数会浪费编码长度
  2. (n<<1)^(n>>31)
  3. -1 将会被编码成 1,1 将会编码成 2,-2 将会被编码成 3
  4. sint32sint64使用 zigzag 编码

message structure 编码

  1. Tag-Value 编码
  2. Tag=(field_number<<3)|wire_type->varints
  3. wire_type:0 表示 varints,1 表示固定 64 位,5 表示固定 32 位
  4. wire_type:2 表示 Tag-Length-Value 编码(TLV),Length 使用 varints
  5. string、bytes、message 嵌套,都采用 TLV 编码

wire_type 只有 0、1、2、5,那么 3 和 4 去哪了?—被废除了

repeated 编码

  1. 第一种方式:重复出现的相同 tag

  2. 第二种方式:(packed=true),TLVVV…编码

    仅有数字类型才可以使用第二种方法,protocol buffers 3(pb3)中默认第二种,pb2 中需要指定,第一种任何情况下会被支持

    repeated情况出现重复 tag,后面的覆盖前面的,因此optionalrepeated相互兼容

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 编译器

  1. C++编写的 proto 文件编译器
  2. 支持各种语言编写的插件,使用进程间通信传递信息
  3. 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:无需反序列化