技术技巧
你也可以将设计和使用相关的问题发送到 Protocol Buffers 讨论组。
常见文件名后缀
通常会以多种不同格式将消息写入文件。我们建议为这些文件使用以下扩展名。
内容 | 扩展名 |
---|---|
文本格式 | .txtpb |
二进制格式 | .binpb |
JSON 格式 | .json |
对于文本格式,.textproto
也很常见,但我们推荐使用 .txtpb
,因为它更简洁。
多消息流式处理
如果你想将多个消息写入同一个文件或流,需要自己跟踪每个消息的起止位置。Protocol Buffer 的二进制格式不是自描述的,因此解析器无法自行判断消息的结束位置。最简单的解决方法是,在写入每个消息前先写入其大小。读取时,先读取大小,再将对应字节读入缓冲区,然后解析该缓冲区。(如果想避免复制字节到单独缓冲区,可以查看 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 {
// 描述类型及其依赖的 FileDescriptorProto 集合。
google.protobuf.FileDescriptorSet descriptor_set = 1;
// 以 Any 消息编码的消息及其类型。
google.protobuf.Any message = 2;
}
通过使用如 DynamicMessage
(C++ 和 Java 均有)等类,你可以编写工具来操作 SelfDescribingMessage
。
需要注意的是,这一功能没有包含在 Protocol Buffer 库中,因为 Google 内部并未遇到相关需求。
该技术需要平台支持基于描述符的动态消息。在使用自描述消息前,请确认你的平台支持此功能。