概述
Protocol Buffers是一种与语言无关、平台无关、可扩展的结构化数据序列化机制-
它类似于JSON,但是更小、更快,并且可以生成原生语言绑定。你只需要定义一次,然后就可以使用特殊的生成
源代码,轻松地从各种数据流中读写结构化数据,并支持多种编程语言
Protocol buffers是一种定义语言(在’.prtot’文件中创建)、用于与数据接口的代码(由proto编译器生成)、
特定语言的运行时库、写入文件(或者通过网络连接发送)的数据的序列化格式,以及序列化数据的组合。
Protocol Buffers解决了什么问题?
Protocol Buffers 提供了一种用于序列化类型化、结构化数据包的格式,这些数据包的大小可达几兆字节。
该格式既适用于短暂的网络流量,也适用于长期数据存储。Protocol Buffers 可以通过添加新信息来扩展,
而不会使现有数据失效或要求更新代码。
Protocol Buffers 是 Google 最常用的数据格式。它们广泛用于服务器间通信以及磁盘上的数据归档存储。
Protocol Buffer 的 消息 和 服务 由工程师编写的 .proto
文件描述。以下是一个示例 消息
:
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 直接支持以下语言:
谷歌支持以下语言,项目的源代码位于GitHub存储库中。protoc 编译器使用这些插件语言:
谷歌不直接支持其他语言,而是其他语言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被序列化时,相同的数据可以有多种不同的二进制序列化形式。您无法在不完全解析它们的情况下比较两个消息是否相等。
- 消息未压缩。虽然消息可以像任何其他文件一样被zip或gzip压缩,但JPEG和PNG等使用的专用压缩算法将为适当类型的数据生成小得多的文件。
- 对于涉及大型多维浮点数数组的许多科学和工程用途,Protocol Buffers消息在大小和速度上都不是最高效的。对于这些应用,FITS和类似格式的开销更小。
- Protocol Buffers在科学计算中流行的非面向对象语言(如Fortran和IDL)中不受良好支持。
- Protocol Buffers消息本身并不自我描述其数据,但它们具有完全反射的架构,您可以使用它来实现自我描述。也就是说,没有相应的
.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, 或
edition 2023的语言指南.
设置基数和数据类型后,您为字段选择一个名称。设置字段名称时需要注意以下几点:
- 在字段名称已在生产中使用后,有时很难甚至不可能更改它们。
- 字段名称不能包含破折号。有关字段名称语法的更多信息,请参阅
消息和字段名称. - 对重复字段使用复数名称
为字段分配名称后,您分配一个字段编号。字段编号不能重复使用或重新分配。
如果您删除一个字段,应该保留其字段编号,以防止有人意外地重新使用该编号。
附加数据类型支持
Protocol Buffers支持许多标量值类型,包括使用可变长度编码和固定大小的整数。
您还可以通过定义本身就是数据类型的消息来创建自己的复合数据类型,这些数据类型可以分配给字段。
除了简单和复合值类型外,还发布了几个常用类型
历史
要了解Protocol Buffers项目的历史,请参阅
Protocol Buffers的历史.
Protocol Buffers开源理念
Protocol Buffers于2008年开源,旨在为Google外部的开发人员提供与我们内部从中受益的相同优势。
我们通过定期更新语言来支持开源社区,就像我们为满足内部需求所做的更改一样。
虽然我们接受来自外部开发人员的选定拉取请求,但我们无法总是优先考虑不符合Google特定需求的功能请求和错误修复。