概述
它类似于 JSON,但更小、更快,并且可以生成原生语言绑定。你只需定义一次数据结构,然后就可以使用专门生成的源代码,轻松地将结构化数据读写到各种数据流中,并支持多种编程语言。
Protocol Buffers 包含定义语言(在 .proto
文件中创建)、proto 编译器生成的用于与数据交互的代码、语言特定的运行时库、用于写入文件(或通过网络连接发送)的序列化格式,以及序列化后的数据。
Protocol Buffers 解决了哪些问题?
Protocol Buffers 提供了一种用于类型化、结构化数据包的序列化格式,适用于几兆字节以内的数据。该格式既适用于临时网络通信,也适用于长期数据存储。Protocol Buffers 可以通过添加新信息进行扩展,而不会使现有数据失效或需要更新代码。
Protocol Buffers 是 Google 内部最常用的数据格式,广泛用于服务器间通信以及磁盘上的数据归档存储。Protocol Buffer 的 消息 和 服务 由工程师编写的 .proto
文件描述。以下是一个 message
示例:
edition = "2023";
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
在构建时,proto 编译器会对 .proto
文件进行处理,生成用于操作对应 Protocol Buffer 的多种编程语言代码(详见后文 跨语言兼容性)。每个生成的类都包含对每个字段的简单访问器,以及用于将整个结构序列化和解析为原始字节的方法。以下是使用这些生成方法的示例:
Person john = Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.build();
output = new FileOutputStream(args[0]);
john.writeTo(output);
由于 Protocol Buffers 被广泛用于 Google 的各种服务,并且其中的数据可能会长期存在,因此保持向后兼容性至关重要。Protocol Buffers 支持无缝更改,包括添加新字段和删除现有字段,而不会破坏现有服务。更多内容请参见后文 无需更新代码即可更新 Proto 定义。
使用 Protocol Buffers 有哪些好处?
Protocol Buffers 非常适合需要以与语言和平台无关、可扩展的方式序列化结构化、记录型、类型化数据的场景。它们最常用于定义通信协议(通常与 gRPC 一起使用)和数据存储。
使用 Protocol Buffers 的优势包括:
- 数据存储紧凑
- 解析速度快
- 支持多种编程语言
- 通过自动生成的类实现优化功能
跨语言兼容性
同一消息可以被任何受支持编程语言编写的代码读取。你可以在一个平台上用 Java 程序采集数据,根据 .proto
定义进行序列化,然后在另一个平台上用 Python 应用程序提取序列化数据中的特定值。
Protocol Buffers 编译器 protoc 直接支持以下语言:
以下语言由 Google 支持,但项目源码托管在 GitHub 上,protoc 编译器通过插件支持:
还有其他语言由第三方 GitHub 项目支持,详见 Protocol Buffers 第三方插件。
跨项目支持
你可以通过将 message
类型定义在项目代码库之外的 .proto
文件中,实现跨项目使用 Protocol Buffers。如果你预计某些 message
类型或枚举会被广泛使用,可以将它们单独放在没有依赖的文件中。
Google 内部广泛使用的 proto 定义示例有
timestamp.proto
和
status.proto
。
无需更新代码即可更新 Proto 定义
软件产品通常要求向后兼容,但很少要求向前兼容。只要在更新 .proto
定义时遵循一些
简单实践,旧代码就能读取新消息,只是会忽略新增字段。对于旧代码,已删除字段会有默认值,已删除的 repeated 字段会为空。有关 “repeated” 字段的说明,请参见后文 Protocol Buffers 定义语法。
新代码也能透明地读取旧消息。新字段在旧消息中不会出现,此时 Protocol Buffers 会提供合理的默认值。
Protocol Buffers 不适用的场景
Protocol Buffers 并不适用于所有数据场景,尤其是:
- Protocol Buffers 假设整个消息可以一次性加载到内存中,且不大于对象图。对于超过几兆字节的数据,建议使用其他方案;处理大数据时,可能会因多次序列化导致内存使用激增。
- Protocol Buffers 序列化后,同一数据可能有多种二进制表示。无法在不完全解析的情况下比较两个消息是否相等。
- 消息本身不压缩。虽然可以像其他文件一样用 zip 或 gzip 压缩,但针对特定类型数据的专用压缩算法(如 JPEG、PNG)会更高效。
- 对于涉及大量多维浮点数组的科学和工程应用,Protocol Buffers 在大小和速度上都不是最优选择。此类应用建议使用 FITS 等格式。
- Protocol Buffers 在 Fortran、IDL 等科学计算常用的非面向对象语言中支持较差。
- Protocol Buffers 消息本身不自描述,但有完整的反射式 schema 可用于实现自描述。即,必须有对应的
.proto
文件才能完全解析。 - Protocol Buffers 不是任何组织的正式标准,因此不适用于有法律或标准化要求的场景。
谁在使用 Protocol Buffers?
许多项目都在使用 Protocol Buffers,包括:
Protocol Buffers 如何工作?
下图展示了使用 Protocol Buffers 处理数据的流程。
图 1. Protocol Buffers 工作流程
Protocol Buffers 生成的代码提供了从文件和流中检索数据、提取单个值、检查数据是否存在、将数据序列化回文件或流等实用方法。
以下代码示例展示了 Java 中的这一流程。如前所示,这是一个 .proto
定义:
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
编译该 .proto
文件会生成一个 Builder
类,可用于创建新实例,如下所示:
Person john = Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.build();
output = new FileOutputStream(args[0]);
john.writeTo(output);
你还可以用 Protocol Buffers 在其他语言(如 C++)中反序列化数据:
Person john;
fstream input(argv[1], ios::in | ios::binary);
john.ParseFromIstream(&input);
int id = john.id();
std::string name = john.name();
std::string email = john.email();
Protocol Buffers 定义语法
定义 .proto
文件时,可以指定字段的基数(单个或 repeated)。在 proto2 和 proto3 中,还可以指定字段是否为 optional。在 proto3 中,将字段设为 optional
会将其从隐式 presence 改为显式 presence。
设置字段基数后,指定数据类型。Protocol Buffers 支持常见的原始数据类型,如整数、布尔值和浮点数。完整列表见 标量值类型。
字段还可以是:
message
类型,可用于嵌套定义(如重复数据集)。enum
类型,可指定一组选项。oneof
类型,适用于有多个可选字段且同一时间最多只设置一个字段的消息。map
类型,可为定义添加键值对。
消息可以通过 扩展 机制定义消息外的字段。例如,protobuf 库的内部消息 schema 允许为自定义选项扩展。
更多选项请参见 proto2、 proto3 或 2023 版 的语言指南。
设置基数和数据类型后,需要为字段命名。命名时需注意:
- 字段名一旦投入生产后更改可能很困难甚至不可能。
- 字段名不能包含短横线。更多语法见 消息和字段命名。
- 对于 repeated 字段,使用复数命名。
命名后,需要分配字段编号。字段编号不能被重复使用。如果删除字段,应保留其编号,防止被误用。
其他数据类型支持
Protocol Buffers 支持多种标量值类型,包括变长编码和定长整数。你还可以通过定义消息类型创建自定义复合数据类型。除了简单和复合类型外,还发布了若干常用类型。
历史
关于 Protocol Buffers 项目的历史,请参见 Protocol Buffers 的历史。
Protocol Buffers 开源理念
Protocol Buffers 于 2008 年开源,旨在让 Google 以外的开发者也能享受其内部带来的好处。我们通过定期更新语言来支持开源社区,同时满足内部需求。虽然我们会接受部分外部开发者的 pull request,但无法始终优先处理不符合 Google 需求的功能请求和 bug 修复。
开发者社区
想要获取 Protocol Buffers 的最新动态,或与 protobuf 开发者和用户交流, 加入 Google Group。