量子计算9 发表于 2022-12-6 15:36

Protocol Buffers 笔记 1 —— 概述

译自https://developers.google.com/protocol-buffers/docs/overview
1 概述

Protocol Buffers提供了一种语言无关、平台无关、可扩展的机制,用于以向前兼容和向后兼容的方式序列化结构化数据。它类似于JSON,只是它更小、更快,并且生成本地语言绑定。
Protocol Buffers是定义语言(在.proto文件中创建)、proto编译器生成的用于与数据交互的代码、特定于语言的运行时库以及写入文件(或通过网络连接发送)的数据的序列化格式的组合。
1.1 Protocol Buffers能解决什么问题?

Protocol Buffers为最大可达几兆字节的类型化结构化数据包提供了一种序列化格式。该格式既适用于短暂的网络流量,也适用于长期的数据存储。Protocol Buffers可以使用新信息进行扩展,而无需使现有数据失效或要求更新代码。Protocol Buffers是谷歌中最常用的数据格式。它们广泛用于服务器间通信以及磁盘上的数据归档存储。Protocol Buffers的消息和服务由工程师编写的.proto文件描述。下面是一个示例message:
message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
}proto编译器在.proto文件构建时被调用,以生成各种编程语言的代码来操作相应的Protocol Buffers。每个生成的类都包含针对每个字段的简单访问器和用于序列化和解析整个结构与原始字节之间的方法。下面是一个使用这些生成方法的示例:
Person john = Person.newBuilder()
    .setId(1234)
    .setName("John Doe")
    .setEmail("jdoe@example.com")
    .build();
output = new FileOutputStream(args);
john.writeTo(output);由于Protocol Buffers在谷歌上广泛应用于各种服务,并且其中的数据可能会持续存在一段时间,因此保持向后兼容性是至关重要的。Protocol Buffers允许对更改的无缝支持,包括向任何Protocol Buffers添加新字段和删除现有字段,而不会破坏现有服务。有关此主题的更多信息,请参阅本主题后面的部分,在不更新代码的情况下更新原定义。
1.2 使用Protocol Buffers的好处是什么

Protocol Buffers非常适合任何需要以语言无关、平台无关、可扩展的方式序列化结构化、类似记录的类型化数据的情况。它们最常用于定义通信协议(与gRPC一起)和数据存储。
使用Protocol Buffers的一些优点包括:

[*]紧凑的数据存储
[*]快速解析
[*]在许多编程语言中的可用性
[*]通过自动生成的类优化功能
跨语言的兼容性
用任何受支持的编程语言编写的代码都可以读取相同的消息。可以让一个平台上的Java程序从一个软件系统捕获数据,根据.proto定义对其进行序列化,然后在另一个平台上运行的独立Python应用程序中从序列化的数据中提取特定的值。
Protocol Buffers编译器protoc直接支持以下语言:

[*]C++
[*]C#
[*]Java
[*]Kotlin
[*]Objective-C
[*]PHP
[*]Python
[*]Ruby
谷歌支持以下语言,但项目源代码位于GitHub存储库中。protoc编译器使用这些语言的插件:

[*]Dart
[*]Go
跨项目的支持
通过在驻留在特定项目代码库之外的.proto文件中定义message类型,您可以跨项目使用Protocol Buffers。如果您定义的消息类型或枚举预计将在您的直接团队之外广泛使用,那么可以将它们放在自己的文件中,不存在依赖关系。
更新proto定义而不更新代码
向后兼容是软件产品的标准,但向前兼容则不太常见。在更新.proto定义时,只要遵循一些简单的实践,旧代码就会毫无问题地读取新消息,忽略任何新添加的字段。对于旧的代码,被删除的字段将有其默认值,被删除的重复字段将为空。有关什么是“重复”字段的信息,请参阅本主题后面的Protocol Buffers定义的语法。
什么时候不适合使用Protocol Buffers?
Protocol Buffer并不适合所有数据。特别是:

[*]Protocol Buffers倾向于假设整个消息可以一次性加载到内存中,并且不大于一个对象图。对于超过几兆字节的数据,考虑不同的解决方案;在处理较大的数据时,由于序列化的副本,您可能最终会得到多个数据副本,这可能会导致内存使用出现惊人的峰值。
[*]当对Protocol Buffers进行序列化时,相同的数据可以有许多不同的二进制序列化。如果不完全解析两条消息,就无法比较它们是否相等。
[*]消息没有被压缩。虽然可以像任何其他文件一样对消息进行压缩或gzip,但JPEG和PNG使用的特殊目的压缩算法将为适当类型的数据生成更小的文件。
[*]对于许多涉及大量多维浮点数数组的科学和工程应用来说,Protocol Buffers消息在大小和速度上都没有达到最大效率。对于这些应用程序,FITS和类似格式的开销更小。
[*]在科学计算中流行的非面向对象语言(如Fortran和IDL)中,Protocol Buffers没有得到很好的支持。
[*]Protocol Buffers消息本身并不自我描述它们的数据,但是它们有一个完全反射模式,您可以使用它来实现自我描述。也就是说,如果不访问它对应的.proto文件,就不能完全解释它。
[*]Protocol Buffers不是任何组织的正式标准。这使得它们不适合在具有基于标准构建的法律或其他要求的环境中使用。
1.3 谁使用Protocol Buffers

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

[*]gRPC
[*]Google Cloud
[*]Envoy Proxy
1.4 Protocol Buffers如何工作

下图显示了如何使用Protocol Buffer处理数据。



Figure 1. Protocol buffers workflow

Protocol Buffers生成的代码提供了从文件和流中检索数据、从数据中提取单个值、检查数据是否存在、将数据序列化回文件或流以及其他有用的函数的实用方法。
下面的代码示例向您展示了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);
john.writeTo(output);然后,你可以使用Protocol Buffers在其他语言中创建的方法反序列化数据,比如c++:
Person john;
fstream input(argv, ios::in | ios::binary);
john.ParseFromIstream(&input);
int id = john.id();
std::string name = john.name();
std::string email = john.email();
1.5 Protocol Buffers定义的语法

在定义.proto文件时,可以指定一个字段是optional或repeated(proto2和proto3)或singular(proto3)。(在proto3中没有将字段设置为required的选项,在proto2中强烈不鼓励。)
在设置了字段的可选性/可重复性之后,您可以指定数据类型。Protocol Buffers支持常见的基本数据类型,如整数、布尔值和浮点数。
一个字段也可以是:

[*]message类型,以便您可以嵌套定义的部分内容,例如用于重复数据集。
[*]enum类型,因此可以指定一组要从中选择的值。
[*]oneof类型,当消息有许多可选字段且同时最多设置一个字段时,可以使用该类型。
[*]map类型,用于向定义中添加键值对。
在proto2中,消息可以允许扩展在消息本身之外定义字段。例如,protobuf库的内部消息模式允许对定制的、特定于使用的选项进行扩展。
有关可用选项的更多信息,请参阅proto2或proto3的语言指南。
设置可选性和字段类型后,分配字段号。字段号不能被重新使用或重用。如果您删除一个字段,您应该保留它的字段编号,以防止有人意外重用该编号。
1.6 附加数据类型支持

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

[*]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美元。
[*]PostalAddress是一个邮政地址,如1600 Amphitheatre Parkway Mountain View, CA 94043 USA。
[*]Color是RGBA颜色空间中的一种颜色。
[*]Month是一年中的一个月,例如四月。
Protocol Buffers开源理念
Protocol Buffers在2008年是开源的,它为谷歌外部的开发人员提供了与我们从内部获得的相同好处。我们通过对语言的定期更新来支持开源社区,因为我们做出了这些改变来支持我们的内部需求。虽然我们接受来自外部开发人员的选择拉请求,但我们不能总是优先考虑不符合谷歌特定需求的特性请求和bug修复。
1.7 附加资源

https://github.com/protocolbuffers/protobuf/
页: [1]
查看完整版本: Protocol Buffers 笔记 1 —— 概述