技巧

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

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

常见文件名后缀

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

内容扩展名
Text Format.txtpb
Wire Format.binpb
JSON 格式.json

具体来说,对于 Text Format,.textproto 也相当常见,但为了简洁起见,我们推荐使用 .txtpb

流式处理多条消息

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

大型数据集

Protocol Buffers 不适合处理大型消息。通常来说,如果你的每条消息大于一兆字节,可能就需要考虑另一种策略了。

尽管如此,Protocol Buffers 非常适合处理大型数据集中的单个消息。通常,大型数据集是许多小片段的集合,每个小片段都是结构化数据。即使 Protocol Buffers 不能一次性处理整个集合,使用 Protocol Buffers 编码每个片段也能极大地简化你的问题:现在你只需要处理一组字节字符串,而不是一组结构。

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

自描述消息

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

但是,.proto 文件的内容本身可以使用协议缓冲区表示。源代码包中的 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 内部从未需要过它。

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