技巧

描述了一些处理 Protocol Buffers 的常用设计模式。

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

常见文件名后缀

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

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

具体到文本格式,.textproto 也相当常见,但我们因其简洁性而推荐使用 .txtpb

流式传输多条消息

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

大数据集

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 内部从未需要使用它。

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