概览
它类似于 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 允许无缝支持对任何 protocol buffer 的更改,包括添加新字段和删除现有字段,而不会破坏现有服务。有关此主题的更多信息,请参阅本主题后面的 在不更新代码的情况下更新 Proto 定义。
使用 Protocol Buffers 的好处是什么?
Protocol buffers 非常适合任何需要以与语言无关、与平台无关、可扩展的方式序列化结构化、记录式、类型化数据的情况。它们最常用于定义通信协议(与 gRPC 一起)和数据存储。
使用 protocol buffers 的一些优点包括
- 紧凑的数据存储
- 快速解析
- 支持多种编程语言
- 通过自动生成的类优化功能
跨语言兼容性
相同的消息可以由用任何支持的编程语言编写的代码读取。你可以让一个平台上的 Java 程序从一个软件系统捕获数据,根据 .proto 定义对其进行序列化,然后在一个在另一个平台上运行的单独的 Python 应用程序中从该序列化数据中提取特定值。
以下语言直接在 protocol buffers 编译器 protoc 中受支持
以下语言由 Google 支持,但项目的源代码位于 GitHub 存储库中。protoc 编译器对这些语言使用插件
其他语言不受 Google 直接支持,而是由其他 GitHub 项目支持。这些语言涵盖在 Protocol Buffers 的第三方附加组件 中。
跨项目支持
你可以通过在特定项目代码库之外的 .proto 文件中定义 message 类型来跨项目使用 protocol buffers。如果你正在定义你预计会在你的直接团队之外广泛使用的 message 类型或枚举,你可以将它们放在自己的文件中,不带任何依赖项。
Google 内部广泛使用的 proto 定义的几个示例是 timestamp.proto 和 status.proto。
在不更新代码的情况下更新 Proto 定义
软件产品通常是向后兼容的,但向前兼容则不那么常见。只要在更新 .proto 定义时遵循一些 简单实践,旧代码将毫无问题地读取新消息,忽略任何新添加的字段。对于旧代码,已删除的字段将具有其默认值,已删除的重复字段将为空。有关“重复”字段的信息,请参阅本主题后面的 Protocol Buffers 定义语法。
新代码也将透明地读取旧消息。新字段将不会出现在旧消息中;在这些情况下,protocol buffers 提供一个合理的默认值。
Protocol Buffers 不适合什么时候?
Protocol Buffers 并不适合所有数据。特别是
- Protocol Buffers 倾向于假设整个消息可以一次性加载到内存中,并且不大于对象图。对于超过几兆字节的数据,请考虑不同的解决方案;处理更大的数据时,由于序列化副本,您最终可能会有效地获得数据的几个副本,这可能会导致内存使用量出现惊人的峰值。
- 当 protocol buffers 序列化时,相同的数据可以有许多不同的二进制序列化。您无法在不完全解析它们的情况下比较两个消息是否相等。
- 消息未压缩。虽然消息可以像其他文件一样进行 zip 或 gzip 压缩,但像 JPEG 和 PNG 使用的专用压缩算法将为适当类型的数据生成更小的文件。
- 对于许多涉及大型多维浮点数数组的科学和工程用途,Protocol Buffer 消息在大小和速度方面都不是最高效的。对于这些应用程序,FITS 和类似格式的开销较小。
- Protocol buffers 在科学计算中流行的非面向对象语言(如 Fortran 和 IDL)中支持不佳。
- Protocol buffer 消息本身不描述其数据,但它们具有一个完全反射的模式,您可以使用它来实现自描述。也就是说,如果没有对其相应
.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 类,您可以使用它来创建新实例,如以下 Java 代码所示
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 文件时,您可以指定基数(单数或重复)。在 proto2 和 proto3 中,您还可以指定字段是否可选。在 proto3 中,将字段设置为可选会将其从隐式存在更改为显式存在。
设置字段的基数后,您指定数据类型。Protocol buffers 支持常见的原始数据类型,例如整数、布尔值和浮点数。有关完整列表,请参阅标量值类型。
字段也可以是
message类型,以便您可以嵌套定义的各个部分,例如用于重复的数据集。enum类型,以便您可以指定一组值以供选择。oneof类型,当消息有许多可选字段且同时最多设置一个字段时,可以使用此类型。map类型,用于向您的定义添加键值对。
消息可以允许扩展在消息本身之外定义字段。例如,protobuf 库的内部消息模式允许为自定义、特定用途的选项提供扩展。
有关可用选项的更多信息,请参阅 proto2、proto3 或 2023 版 的语言指南。
设置基数和数据类型后,您为字段选择一个名称。设置字段名称时需要记住一些事项
- 在生产中使用后,有时很难甚至不可能更改字段名称。
- 字段名称不能包含破折号。有关字段名称语法的更多信息,请参阅消息和字段名称。
- 对重复字段使用复数名称。
为字段分配名称后,您分配一个字段编号。字段编号不能重新利用或重用。如果您删除一个字段,您应该保留其字段编号以防止有人意外重用该编号。
额外的数据类型支持
Protocol buffers 支持多种标量值类型,包括使用可变长度编码和固定大小的整数。您还可以通过定义自身是数据类型的消息来创建自己的复合数据类型,您可以将其分配给字段。除了简单和复合值类型之外,还发布了几个常见类型。
历史
要阅读 protocol buffers 项目的历史,请参阅Protocol Buffers 的历史。
Protocol Buffers 开源理念
Protocol buffers 于 2008 年开源,旨在为 Google 之外的开发者提供我们内部所获得的相同好处。我们通过对语言的定期更新来支持开源社区,因为我们对语言进行了这些更改以支持我们的内部需求。虽然我们接受外部开发者的一些拉取请求,但我们不能总是优先考虑不符合 Google 特定需求的功能请求和错误修复。
开发者社区
要了解 Protocol Buffers 的即将更改并与 protobuf 开发者和用户联系,请加入 Google Group。