这篇文章提供了一份使用Protocol-Buffers的注意细节, 其实就是类似于其他语言中的最佳实践, 由一些条目组成. 其实老外的文章并不一定都是那么的好, 哪怕是google的工程师, 他们写出来的这些文章页并不一定就是字字珠玑, 其实也有很多啰嗦和不清楚的地方. 所以, 我在这里先总结一个自己阅读这些文章中读到的推荐的使用规则.
1. required 相当于数据库中的那些not null的列. required 的域不利于协议的扩展. 所以推荐使用optional, 当然required也有适用的地方.
2. Protocol-Buffers只适合于小型消息.
3. Protocol-Buffers的数据传输格式是不含结尾符的, 在多个消息之间的间隔要自己去实现.
4. 1~15 的tag值占用的空间最少.
5. 一定要看一下Protocol-Buffers 编码原理(英), 知其所以然, 才能用好, 后面我会翻译(好了, 呵呵).
下面是译文:
技巧
本文描述了在使用Protocol-Buffers时常用的设计模式, 惯用法. 你也可以去Protocol-Buffers讨论组提出关于Protocol-Buffers的设计和使用的问题, 并获得解答.
多消息转化为流
如果你想将多个消息写入单个文件或者流中, 那么你必须自己来记录跟踪一个消息和下一个消息边界. Protocol-Buffers的数据传输格式是不含结尾符的, 所以Protocol-Buffers解析器无法确定一个消息的结尾. 解决该问题的最简单方案就是在写入一个消息之前, 先写入该消息的长度大小, 当你读取消息的时候, 先读取消息的大小, 然后读入该大小字节数的数据读入一个独立的缓冲区中, 然后解析数据. 如果你想避免到独立缓冲区中的拷贝, 请看CodedInputStream类的使用方法 — 你可以用它来限制只读取指定大小字节的数据.
大数据集
Protocol-Buffers不是设计来处理大消息的. 根据经验, 如果单条消息大于1M, 那么就应该采取其他的策略.
相反Protocol-Buffers 很适合处理一个大数据集内有多个单独的消息的情况. 通常大数据集是许多小数据的集合, 每个小数据块都是一块结构化数据. Protocol-Buffers并不能一次就处理整个数据集, 然而使用Protocol-Buffers 来编码每个小数据块可以很大的简化你的问题, 现在只需要处理一组字符串, 而不是一组结构体了.
Protocol-Buffers 没有内置任何对大数据集的支持, 因为不同的应用情况需要不同的解决方案. 有时一个简单的记录列表就能解决问题, 而在其他情况下, 可能使用数据库是一种更好的选择. 对每个解决方案都开发一个独立的程序库的代价是很大的.
联合类型 (用处不大)
有时你可能想发送一个消息, 其类型可以是几个不同的类型之一. 然而Protocol-Buffers 解析器无法仅凭消息内容来决定消息的类型. 这种情况下, 你如何确保接收方应用能知道怎么解析消息呢? 一个解决方案是创建一个封装的消息, 其中含N个optional的字段, 每一个字段对应一种消息类型.
例如, 如果你有消息类型Foo, Bar, Baz, 那么你可以按类型将它们结合起来, 就如:
message OneMessage {
optional Foo foo = 1;
optional Bar bar = 2;
optional Baz baz = 3;
}
也可以添加一个枚举字段来标识拿一个消息被填充了, 这样的话就可以使用switch来处理:
message OneMessage {
enum Type {FOO = 1; BAR = 2; BAZ = 3;}
required Type type = 1;
optional Foo foo = 2;
optional Bar bar = 3;
optional Baz baz = 4;
}
如果你有大量可能的类型, 那么你的容器类型中将它们一一列举出来可能比较困难, 在这种情况下, 你应该考虑使用扩展:
message OneMessage {
extensions 100 to max;
}
extend OneMessage {
optional Foo foo_ext = 100;
optional Bar bar_ext = 101;
optional Baz baz_ext = 102;
}
注意你可以使用 ListFields 反射函数来获取消息中所有字段的列表, 其中包含扩展. 这个功能在实现分派消息句柄时用的上它.
自描述的消息 (没啥用, 还绕口, 不推荐看)
Protocol-Buffers 不包含自我描述的信息. 因此如果只提供序列化后的二进制数据, 而不提供对应的.proto文件的话, 很难从中提取什么有用的信息.
然而, 一个,proto文件消息定义格式本身也可以使用Protocol-Buffers格式来描述, 源码包中的src/google/protobuf/descriptor.proto 文件定义了Protocol-Buffers消息格式. 通过使用 -descriptor_set_out 选项, protoc编译器可以输出一个FileDescriptorSet — 这个集合表示一系列的.proto文件. 利用它, 可以定义一个自描述的协议消息.
message SelfDescribingMessage {
required FileDescriptorSet proto_files = 1;
required string type_name = 2;
required bytes message_data = 3;
}
通过使用DynamicMessage 这样的类, 就可以编写出操作自描述消息的工具.
总之, Protocol-Buffers库之所以没有包含这个特性, 是因为我们在Google里面还从来没有使用它的机会.