从 JSON 开始
谈到序列化,大家最先想到的可能是 JSON 或者 XML,这两种序列化协议都是基于文本的编码方式进行数据传输。类似的还有 YAML 等。
JSON 拥有许多优点,使之成为最广泛使用的序列化协议之一。如 JSON 协议简单,人眼可读,序列化后十分简洁且解析速度快。此外,JSON 具备 JavaScript 的先天性支持,被广泛应用于 Web Browser 的应用场景中,并且是 Ajax 的事实标准协议。
JSON 的适用场景比较多,典型应用场景包括:
- 公司外部之间传输数据量相对较小,实时性要求相对低的服务
- 基于 Web browser 的 Ajax 请求
- 接口经常发生变化,并对可调式性要求较高的场景,例如移动 App 与服务端的通信
然而,由于 JSON 本身的设计的一些特点,在一些场景下使用 JSON 仍然不是最优解。如:
- 需要标准的 IDL ,增强参与各方业务约束的场景。由于 JSON 协议往往只能使用文档的方式来进行约定,这可能会给调试带来一些不便与不明确
- 对性能和简洁性有较高要求的场景。JSON 在一些语言中的序列化和反序列化需要采用反射机制,所以在性能要求特别高场景下可能不是最优解
- 对于大数据量服务或持久化场景。JSON 进行序列化的额外空间开销比较大,这也意味着较大的内存和磁盘开销
对于以上场景, 使用一些基于 IDL ,存储方案为二进制存储的序列化方案则更为合适, 如 ProtoBuf、Thrift、avro 等。
IDL: 参与通讯的各方需要对通讯的内容需要做相关的约定。为了建立一个与语言和平台无关的约定,这个约定需要采用与具体开发语言、平台无关的语言来进行描述。这种语言被称为接口描述语言(IDL),采用 IDL 撰写的协议约定称之为 IDL 文件。
什么是 Protobuf
ProtoBuf 是 Protocol Buffers 的简称 ,是 Google 公司开源的一种语言无关、平台无关、可扩展的序列化结构数据的方案,它可用于(数据)通信协议、数据存储等。
ProtoBuf 是上述场景中比较适用的序列化方案之一。 ProtoBuf 非常灵活,高效,我们可以通过定义 IDL(在这里是 proto)文件,然后使用生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。
上文提到,同类型的序列化方案还有 Thrift 和 Avro。其中 Thrift 并不仅仅是序列化协议,他被嵌入到 Thrift 框架中,这导致其很难和其他传输层协议共同使用。Avro 由于没有成熟的 JS 实现,不适合 Web 环境, 也 导致其使用场景也比较有限。
目前 gRPC 默认的序列化方式是 ProtoBuf。
ProtoBuf 包含序列化格式的定义、各种语言的库以及一个 IDL 编译器。正常情况下需要我们定义 proto 文件,然后使用 IDL 编译器编译成需要的语言。
一个简单的 proto 例子
OneofMapReserved
生成 Go 代码
.protoprotoc.proto
protoc
protoc-gen-go
proto
protoc-I
test.pb.go
接下来,就可以在项目代码中直接使用了。
gogo/protobuf 是什么
protoc-gen-gogogo/protobufgolang/protobuf
gogo 库基于官方库开发,增加了很多的功能,包括:
- 快速的序列化和反序列化
- 更规范的 Go 数据结构
- goprotobuf 兼容
- 可选择的产生一些辅助方法,减少使用中的代码输入
- 可以选择产生测试代码和 benchmark 代码
- 其它序列化格式
目前很多知名的项目都在使用该库,如 etcd、k8s、tidb、docker swarmkit 等。
gogo/protobuf 如何使用
https://github.com/gogo/protobuf 根目录下我们可以看到有很多文件夹,其中「protoc-gen」为前缀的为生成代码的插件,其他「proto」、「protobuf」、「gogoproto」等为库文件。
gogo 库目前有三种生成代码的方式
gofast
gogofastgogofastergogoslickgogofastgofastgogofastergogofastXXX_unrecognizedgogoslickgogofasterstringgostringequal method
protoc-gen-gogo
gogo/protobuf 提供了非常多的扩展选项,以便在产生代码的时候进行更多的控制。上文提到的扩展选项这里有一个全面的介绍:extensions,扩展选项里主要包含一些生成快速序列化反序列化代码的可选项、生成更规范的 Golang 数据结构的可选项、goprotobuf 兼容的可选项,一些产生辅助方法的可选项、产生测试代码和 benchmark 的可选项,还可以增加 jsontag 等。
protoc-gen-gofast
proto.Marshalproto.Unmarshal