Protobuf 是 Google 出品的结构化数据格式。特点是 语言中立、平台中立。下面的内容基于 pb3 在 2020 年 10 月的版本。
流程
- 写一个结构定义文件
- 用 protoc 将其生成为编程语言相应的代码
- 配合 protobuf 各语言的 runtime 就可以使用
上手指南
- 根据 指引 安装 protoc 以及你要使用的语言相应的 runtime
- 根据 language guide 理解一个 .proto 文件的基础写法,写好后用 protoc 生成代码文件
- 找到你对应语言的 generated code 文档,了解生成的代码文件 API 如何使用
Language Guide
这份 cheat sheet 简要地描述了 proto 文件的语言规则。
pb3 去掉了 required
/ optional
标识(在 pb2 中存在),一切都是 optional。
数值类型根据值的特点会有不同的编解码性能。比如对于负数,int32 的编解码性能比 sint32 差。根据数值的分布情况来选择数值类型。
对于没打包的字段,解包时会默认用其类型的 默认值,比如数值类型是 0,字符串类型是空字符串。由于这个默认值规则,因此 enum 类型的第一个值必须是 0。
enum 类型支持 alias(STARTED
、RUNNING
值都为 1),但需要显式打开选项:
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
可以为 Message 或者 enum 类型保留某些 tag number 或是字段名,使其不能被使用:
enum Foo {
reserved 2, 15, 9 to 11, 40 to max;
reserved "FOO", "BAR";
}
字段类型一般是不能修改的(有兼容性问题),但一些 情况 例外(主要是数值类型)。
可以引入别的 proto 文件中的类型:
// 该文件定义了自己的 package:google.protobuf
import "google/protobuf/any.proto";
// import public 表示,当其他 proto 文件引用本文件时,other.proto 中的类型都会暴露出去
import public "other.proto";
message Message {
// 引用 import 进来的消息类型时,需要用它的全称
repeated google.protobuf.Any details = 1;
}
使用 Any 类型时,可以将任意一个 message 类型的值打包进 Any 字段。
Oneof 表示 message 里面,某些字段只能有一个字段被设置上值。
Protobuf 的结构可以 跟 JSON 互相转换。
Message 不支持继承。
Well-Known Types
Google 提供了一些常用的类,称之为 well-known types,也用 proto 文件表达。使用时需要在 proto 文件中 import 进来:
import "google/protobuf/timestamp.proto";
message Person {
string name = 1;
// ...
google.protobuf.Timestamp last_updated = 5;
}
但我觉得里面没多少好用的东西。
Best Practices
团队应该维护一个公共的基础 message 库,比如定义业务相关的基础数据类型(比如用户),通用的结构(比如时间)等。
Uber 在 pb 上耕耘了很久,prototool 和 buf 非常值得一看(我还没看)。
这份 文档 提供了 PB 相关的编程语言库、工具、RPC 实现等。