注意:此网站已被弃用。该网站将在 2023 年 1 月 31 日后关闭,而流量将重定向到位于 https://protobuf.dev 的新网站。在此期间,我们仅会针对 protobuf.dev 进行更新。

概览

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

协议缓冲区提供了一种与语言和平台无关的可扩展机制,用于以向前兼容和向后兼容的方式序列化结构化数据。它与 JSON 类似,只不过它更小、更快,并且可以生成原生语言绑定。

协议缓冲区由定义语言(在 .proto 文件中创建)、proto 编译器生成的用于与数据进行交互的代码、特定语言的运行时库,以及写入文件(或通过网络连接发送)的数据的序列化格式组合。

协议缓冲区可以解决哪些问题?

协议缓冲区为类型最多为几 MB 的结构化数据类型的数据包提供序列化格式。此格式同时适用于临时网络流量和长期数据存储。协议缓冲区可以使用新信息扩展,而不必使现有数据失效,也无需更新代码。

协议缓冲区是 Google 最常用的数据格式。它们广泛用于服务器间通信以及磁盘上的归档数据存储。协议缓冲区消息服务由工程师编写的 .proto 文件描述。下面显示了一个示例 message

message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional string email = 3;
}

proto 编译器会在构建时对 .proto 文件调用,以生成各种编程语言的代码(如需了解详情,请参阅本主题后面的跨语言兼容性部分),以便处理相应的协议缓冲区。每个生成的类都包含每个字段的简单访问器以及将整个结构序列化和解析到原始字节的方法。下面展示了使用这些生成的方法的示例:

Person john = Person.newBuilder()
    .setId(1234)
    .setName("John Doe")
    .setEmail("jdoe@example.com")
    .build();
output = new FileOutputStream(args[0]);
john.writeTo(output);

由于协议缓冲区广泛用于 Google 的所有服务,并且其中的数据可能会保留一段时间,因此保持向后兼容性至关重要。借助协议缓冲区,您可以无缝支持对任何协议缓冲区的更改(包括添加新字段和删除现有字段),而不会中断现有服务。如需详细了解此主题,请参阅本主题后面的更新代码而不更新代码

使用协议缓冲区有哪些益处?

如果您需要以与语言无关、平台中立、可扩展的方式对结构化、类记录的类型化数据进行序列化,则非常适合使用协议缓冲区。它们通常用于定义通信协议(与 gRPC 结合使用)和数据存储。

使用协议缓冲区的一些优点包括:

  • 紧凑型数据存储
  • 快速解析
  • 支持多种编程语言
  • 通过自动生成的类优化功能

跨语言兼容性

用任何受支持的编程语言编写的代码可以读取相同的消息。您可以让一个平台有一个 Java 程序从一个软件系统捕获数据,根据 .proto 定义对其进行序列化,然后在另一个平台上运行的单独 Python 应用中从该序列化数据中提取特定值。

协议缓冲区编译器 protoc 直接支持以下语言:

Google 支持以下语言,但项目的源代码位于 GitHub 代码库中。protoc 编译器对以下语言使用插件:

其他语言由 Google 直接支持,但由其他 GitHub 项目提供支持。协议缓冲区的第三方插件中介绍了这些语言。

跨项目支持

通过在位于特定项目代码库之外的 .proto 文件中定义 message 类型,您可以在不同项目之间使用协议缓冲区。如果您要定义将在直接团队之外广泛使用的 message 类型或枚举,可以将它们放在不含任何依赖项的文件中。

以下是在 Google 内部广泛使用的 proto 定义示例:timestamp.protostatus.proto

无需更新代码即可更新 Proto 定义

软件产品向后兼容是标准标准,但这类产品不太向前兼容。只要您遵循更新 .proto 定义时遵循的一些简单做法,旧代码就会直接读取新消息,而不会发现任何新添加的字段。对于旧代码,已删除的字段将具有默认值,而已删除的重复字段将为空。如需了解“重复”字段的含义,请参阅本主题后面的协议缓冲区定义语法

新代码还会透明地读取旧消息。新字段不会出现在旧消息中;在这些情况下,协议缓冲区会提供合理的默认值。

什么情况下不适合使用协议缓冲区?

协议缓冲区无法容纳所有数据。具体而言:

  • 协议缓冲区假定整个消息可以一次加载到内存中,并且不大于对象图。对于超过几兆字节的数据,请考虑采用不同的解决方案;使用较大的数据时,由于序列化副本,您最终可能会得到多个数据副本,这可能会导致内存用量激增。
  • 协议缓冲区序列化时,相同的数据可能会有许多不同的二进制序列化。不完全解析两条消息,就无法比较相等性。
  • 邮件不会被压缩。虽然消息可以像其他任何文件一样进行压缩或 gzip 处理,但特殊用途的压缩算法(如 JPEG 和 PNG 使用的算法)会针对相应类型的数据生成小得多的文件。
  • 对于涉及大型多维浮点数数组的科学和工程用途,协议缓冲区消息在大小和速度方面均不尽最大。对于这些应用,FITS 和类似格式的开销较低。
  • 在科学计算中常用的非对象语言(例如 Fortran 和 IDL)中并不支持协议缓冲区。
  • 协议缓冲区消息本身没有自我描述,但它们具有完全反射架构,您可以利用这些架构来实现自身描述。也就是说,如果您没有访问相应的 .proto 文件,则无法完全解读该类文件。
  • 协议缓冲区不是任何组织的正式标准。这使得它们不适合在基于标准或其他法律构建环境的环境中使用。

谁会使用协议缓冲区?

许多外部可用的项目使用协议缓冲区,其中包括:

协议缓冲区如何工作?

下图显示了如何使用协议缓冲区来处理数据。


图 1. 协议缓冲区工作流

协议缓冲区生成的代码提供了一些实用工具方法,用于从文件和流中检索数据、从数据中提取各个值、检查数据是否存在,以及将数据序列化回文件或流,以及其他实用函数。

以下代码示例使用 Java 展示了此流程的示例。如前所述,这是 .proto 定义:

message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional 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);

然后,您可以使用协议缓冲区在其他语言(如 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();

协议缓冲区定义语法

定义 .proto 文件时,您可以将字段指定为 optionalrepeated(proto2 和 proto3)或 singular (proto3)。(proto3 中未提供将字段设置为 required 的选项,因此不建议在 proto2 中设置字段。如需了解详情,请参阅指定字段规则中的“必需永久”。)

设置字段的可选性/可重复性后,您可以指定数据类型。协议缓冲区支持常见的原始数据类型,例如整数、布尔值和浮点数。如需查看完整列表,请参阅标量值类型

字段还可为:

  • message 类型,以便嵌套部分定义,例如用于重复数据集。
  • enum 类型,因此您可以指定一组值并选择。
  • oneof 类型,当消息包含多个可选字段并且最多只能设置一个字段时,可以使用此类型。
  • map 类型,用于向您的定义添加键值对。

在 proto2 中,消息可以允许扩展定义消息本身之外的字段。例如,protobuf 库的内部消息架构允许扩展特定于用法的自定义选项。

如需详细了解可用选项,请参阅 proto2proto3 的语言指南。

设置可选字段和字段类型后,您需要指定一个字段编号。字段编号不能重复使用或重复使用。如果您删除某个字段,则应保留其字段编号,以防止有人意外重复使用该编号。

其他数据类型支持

协议缓冲区支持许多标量值类型,包括同时使用可变长度编码和固定大小的整数。您还可以通过定义消息本身来创建复合数据类型,这些消息本身就是可分配给字段的数据类型。除了简单和复合值类型之外,还发布了几种常见类型。

常见类型

  • Duration 是有符号的固定长度时间跨度,例如 42 秒。
  • Timestamp 是独立于任何时区或日历的时间点,例如 2017-01-15T01:30:15.01Z。
  • Interval 是与时区或日历无关的时间间隔,例如 2017-01-15T01:30:15.01Z - 2017-01-16T02:30:15.01Z。
  • Date 是完整的日历日期,例如 2025-09-19。
  • DayOfWeek 是一周中的某一天,例如星期一。
  • TimeOfDay 是一天中的时段,例如 10:42:23。
  • LatLng 是一个纬度/经度对,如 37.386051 纬度和 -122.083855 经度。
  • Money 是货币类型的金额,例如 42 USD。
  • PostalAddress 是邮政地址,例如 1600 Amphitheatre Parkway Mountain View, CA 94043 USA。
  • Color 是 RGBA 颜色空间中的颜色。
  • Month 表示一年中的一个月,例如 4 月。

Protocol Buffer 开源理念

协议缓冲区于 2008 年开放,目的是为 Google 以外的开发者提供我们从内部获得的相同优势。我们会定期更新语言,以便为开源社区提供支持,以满足我们的内部需求。虽然我们接受来自外部开发者的精选拉取请求,但我们无法总是优先处理不符合 Google 具体需求的功能请求和 bug 修复。

其他资源