跳到主要内容

Protocol Buffers

使用描述型文件.proto 生成多种语言版本的控制接口代码。

优点

  • 更紧凑的数据存储
  • 解析速度快
  • 可用于多种语言
  • 自动生成功能丰富的控制类

缺点

  • 大数据。超过几 mb 的数据,因为序列化拷贝时会产生数据的数个副本,消耗大量内存
  • 需要比较。数据序列化后可能产生不同的序列化结果,在完全解析前无法比较
  • 不会被压缩。特定的压缩算法往往效果比 protobuf 更好
  • 大型多维浮点数组,在空间和速度上都比不过专门处理这种类型的工具
  • 不适合非面向对象的语言
  • 不是自解释的语言,需要访问.proto 文件
  • 不是任何组织的正式标准,不适合用在有法律或者其他要求的环境

工作流程

  1. 创建.proto 文件,并定义数据结构
  2. 使用 protoc 编译器生成代码
  3. 将 protobuf 的代码和项目代码一起编译
  4. 使用 protobuf 类在不同语言间进行数据的序列化和反序列化

语法

  • package: 包声明,防止命名冲突,在 C++ 中会作为生成类的 namespace
  • message: 信息数据结构,支持普通数据结构,也支持其他message类型的结构,可以嵌套
    • optional: 可缺省的field, 若没有,将会使用默认值,通过[default = xxx]指定,若无指定,则指定系统默认值
      • numeric: 0
      • bytes: empty bytes
      • strings: ''
      • bools: false
      • enums: 第一个被定义的枚举值,必须为 0
      • 嵌合类型:field不会被 set?,具体值根据语言不同而不同
    • repeated: 可以重复任意次 (包括 0) 的field, 重复项的顺序会被保留,可视为动态数组
    • required: 必须提供的field, 否则信息会被视为uninitialized. 在debug模式中编译,线性化一个未初始化的量会触发断言。在优化模式下编译,则不会被检查,信息会被直接写入,但是解析一个uninitialized的信息永远会失败,会返回false. 其他同optional
      • 使用 required 会造成向后兼容问题,最好不用,在 proto3 中已被删除
    • map(proto 3): 键值对field, 包含repeated
    • enum: 枚举,可以在预先确定的类型中选一个,field number 从 0 开始
    • =1, =2 (field number): 在每个field中唯一的数字,用于数据线性化,数字 1-15 在序列化时,比别的数字需要的空间少一字节,可以通过给常用的数据安排 1-15 的序列,优化空间使用。每个repeated类型的领域需要重编码field number, 所以尤其适合这种优化.?

注意

  • 在删除时field时,需要使用reserved保留 field number 和 field name 以防止复用,因为在线性化时会使用到 field number,如果使用相同的 field number,不同的定义解析,会造成错误。
  • 可通过复用信息对象来节省内存

编码格式

Varint

Varint 编码是一种可变长的编码方式,值越小的数字可以用越少的字节表示

  • field-number: message 中最后一列的数字
  • wire-type: 编码类型,varint 的编码类型为 0
  • msb: most significant bit, 每个字节的最高位
  • Tag 信息: 主要储存 field-numberwire-type
  • Data 信息: 编码后的序列

Varint 编码序列 = Tag 信息 + Data 信息