Protocol Buffers:Techniques

   使用Protocol-Buffers很久了, 现在所在的公司中虽然没有直接使用它, 但是也有自己的和Protocol-Buffers异曲同工的序列/反序列化工具.  最近决定翻译一下Protocol-Buffers的官网上的文章, 增加自己对它细节的理解, 也可以方便需要的人.

     这篇文章提供了一份使用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里面还从来没有使用它的机会.

英文地址: Protocol-Buffers Techniques

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值