技巧

介绍了一些处理 Protocol Buffers 的常用设计模式。

您也可以将设计和使用问题发送到 Protocol Buffers 讨论组

常见文件名后缀

将消息以多种不同格式写入文件是相当常见的做法。我们建议为这些文件使用以下文件扩展名。

内容扩展名
文本格式.txtpb
Wire 格式.binpb
JSON 格式.json

具体到文本格式,.textproto 也相当常见,但我们推荐使用 .txtpb,因为它更简洁。

流式传输多条消息

如果您想将多条消息写入单个文件或流中,您需要自行记录一条消息的结束位置和下一条消息的开始位置。Protocol Buffer 的 wire 格式不是自定界的,因此协议缓冲区解析器无法自行确定消息的结束位置。解决此问题的最简单方法是在写入每条消息之前,先写入该消息的大小。当您读回消息时,先读取大小,然后将相应字节数读入一个单独的缓冲区,再从该缓冲区进行解析。(如果您想避免将字节复制到单独的缓冲区,可以查看 C++ 和 Java 中的 CodedInputStream 类,它可以被告知将读取限制在一定的字节数内。)

大数据集

Protocol Buffers 并非设计用于处理大型消息。根据经验,如果您处理的消息大小超过 1MB,可能就该考虑其他策略了。

话虽如此,Protocol Buffers 非常适合用于处理大型数据集*中*的单个消息。通常,大型数据集是许多小片段的集合,每个小片段都是结构化数据。尽管 Protocol Buffers 无法一次性处理整个数据集,但使用它来编码每个片段可以极大地简化您的问题:现在您只需要处理一组字节字符串,而不是一组结构体。

Protocol Buffers 没有内置对大型数据集的支持,因为不同的情况需要不同的解决方案。有时一个简单的记录列表就足够了,而其他时候您可能需要更像数据库的东西。每种解决方案都应作为独立的库来开发,这样只有需要它的人才需要付出成本。

自描述消息

Protocol Buffers 不包含对其自身类型的描述。因此,如果只有一条原始消息而没有相应的定义其类型的 .proto 文件,很难从中提取任何有用的数据。

然而,.proto 文件的内容本身可以用 protocol buffers 来表示。源代码包中的 src/google/protobuf/descriptor.proto 文件定义了所涉及的消息类型。protoc 可以使用 --descriptor_set_out 选项输出一个 FileDescriptorSet——它表示一组 .proto 文件。有了这个,您就可以像这样定义一个自描述的协议消息

syntax = "proto3";

import "google/protobuf/any.proto";
import "google/protobuf/descriptor.proto";

message SelfDescribingMessage {
  // Set of FileDescriptorProtos which describe the type and its dependencies.
  google.protobuf.FileDescriptorSet descriptor_set = 1;

  // The message and its type, encoded as an Any message.
  google.protobuf.Any message = 2;
}

通过使用像 DynamicMessage(在 C++ 和 Java 中可用)这样的类,您就可以编写能够操作 SelfDescribingMessage 的工具。

尽管如此,Protocol Buffer 库中不包含此功能的原因是,我们在 Google 内部从未有过使用它的需求。

此技术需要支持使用描述符的动态消息。在使用自描述消息之前,请检查您的平台是否支持此功能。