技巧
您也可以将设计和使用问题发送到 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 内部从未需要使用它。
此技术需要支持使用描述符的动态消息。在使用自描述消息之前,请检查您的平台是否支持此功能。