Protocol Buffers学习(概览)

Protocol Buffers概览

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



Protocol Buffers是解决什么问题的?

Protocol Buffers为大小高达几兆字节的类型化结构化数据包提供了一种序列化格式。 该格式适用于临时网络流量和长期数据存储。 可以使用新信息扩展协议缓冲区,而无需使现有数据无效或需要更新代码。
Protocol Buffers是 Google 最常用的数据格式。 它们广泛用于服务器间通信以及磁盘上数据的归档存储。 协议缓冲区消息和服务由工程师编写的 .proto 文件描述。 下面显示了一个示例消息:

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[0]);
john.writeTo(output);

由于Protocol Buffers在 Google 的各种服务中广泛使用,并且其中的数据可能会保留一段时间,因此保持向后兼容性至关重要。 协议缓冲区允许无缝支持对任何Protocol Buffers的更改,包括添加新字段和删除现有字段,而不会破坏现有服务。 有关此主题的更多信息,请参阅本主题后面的在不更新代码的情况下更新原型定义。

使用Protocol Buffers有哪些好处?

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

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

跨语言兼容性

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

Protocol Buffers编译器 protoc 直接支持以下语言:

  • c++
  • c#
  • Java
  • JavaScript
  • Kotlin
  • Objective-C
  • PHP
  • Python
  • Ruby

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

  • Dart
  • Go
    其他语言不直接由 Google 支持,而是由其他 GitHub 项目支持。 这些语言包含在 Protocol Buffers 的第三方插件中。

跨项目支持

您可以通过在驻留在特定项目代码库之外的 .proto 文件中定义消息类型来跨项目使用Protocol Buffers。 如果您正在定义您预计将在您的直接团队之外广泛使用的消息类型或枚举,您可以将它们放在自己的文件中,而无需依赖。
在 Google 中广泛使用的几个原型定义示例是 timestamp.proto 和 status.proto。

在不更新代码的情况下更新原型定义

软件产品向后兼容是标准,但向前兼容却不太常见。 只要您在更新 .proto 定义时遵循一些简单的做法,旧代码将毫无问题地读取新消息,而忽略任何新添加的字段。 对于旧代码,删除的字段将具有默认值,删除的重复字段将为空。 有关什么是“重复”字段的信息,请参阅本主题后面的协议缓冲区定义语法。
新代码也将透明地读取旧消息。 旧消息中不会出现新字段; 在这些情况下,Protocol Buffers提供了一个合理的默认值。

什么时候Protocol Buffers不是一个很好的选择?

协议缓冲区并不适合所有数据。 特别是:

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

谁在使用Protocol Buffers

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

  • gRPC
  • Google Cloud
  • Envoy Proxy

Protocol Buffers如何工作?

下图显示了如何使用协议缓冲区来处理数据。
Protocol Buffers工作流程
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[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)。 (将字段设置为必需的选项在 proto3 中不存在,并且在 proto2 中强烈建议不要使用。有关此内容的更多信息,请参阅指定字段规则中的“必填项”。)
设置字段的可选性/可重复性后,指定数据类型。 协议缓冲区支持通常的原始数据类型,例如整数、布尔值和浮点数。 有关完整列表,请参阅标量值类型。
一个字段也可以是:

  • 一种message类型,以便您可以嵌套部分定义,例如用于重复数据集。
  • enum类型,因此您可以指定一组值以供选择。
  • oneof 类型,当消息有多个可选字段且最多同时设置一个字段时,可以使用该类型。
  • map类型,用于将键值对添加到您的定义中。
    在 proto2 中,消息可以允许扩展定义消息本身之外的字段。 例如,protobuf 库的内部消息模式允许扩展自定义的、特定于使用的选项。
    有关可用选项的更多信息,请参阅 proto2 或 proto3 的语言指南。

在设置可选性和字段类型后,您分配一个字段编号。 字段编号不能改变用途或重复使用。 如果您删除一个字段,您应该保留其字段编号,以防止有人意外重复使用该编号。

额外的数据类型支持

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于 2008 年开源,旨在为 Google 以外的开发人员提供与我们在内部从中获得的相同好处的一种方式。 我们通过定期更新语言来支持开源社区,因为我们进行这些更改以支持我们的内部需求。 虽然我们接受来自外部开发人员的精选拉取请求,但我们不能总是优先考虑不符合 Google 特定需求的功能请求和错误修复。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值