See You Again

使用GPB协议

最近一个项目的数据通讯使用了GPB(Google Protocol Buffer)协议,听说这个协议是在Google内部已经是一个标准了,借此机会刚好了解一下。

为什么要使用GPB协议?

与常用的数据通讯、存储方式相比,优点如下:

实战

上面只是一些感性的认知,下面结合 Python 的 google.protobuf 模块做一些深入讨论。
首先新建一个文件 person.proto,假设这个是某接口的通讯协议——要传 name 和 id 两个Field(都是必填字段)

  1. message PERSON {
  2. required string name = 4;
  3. required int32 id = 5;
  4. }

有了这个协议的源文件,Python还不能直接使用,需要通过工具编译成 py 文件才能用。不像XML、JSON直接用纯文本文件,它还要多经过一步‘编译过程’以适应不同的程序语言(C++、Java、Python)

  1. protoc -I=. --python_out=./GPB GPB/person.proto

执行后会发现 GPB 子目录下多了一个文件 person_pb2.py,这个文件就是 person.proto经过编译得到的文件,可以直接在 Python 中引用。下面用几行代码实现写入一个PERSON对象,然后再读出来,中途把GPB字符串的原始内容打印出来(注意两个核心方法SerializeToStringParseFromString都是 google.protobuf模块提供的):

  1. from GPB import person_pb2
  2. person_write = person_pb2.PERSON(name='shaw', id=15)
  3. s = person_write.SerializeToString()
  4. print len(s), "%r" % s
  5. person_read = person_pb2.PERSON()
  6. person_read.ParseFromString(s)
  7. print person_read

可以看到输出:

  1. 8 '"\x04shaw(\x0f'
  2. name: "shaw"
  3. id: 15

可以看到GPB只用了8个字节传输。如果是JSON格式传输{"name":"shaw","id":15}则要23个字节,更不要提XML了!更进一步,为什么GPB能这么省呢,原因也大概猜的到,它没有传 “name” “id”这些键值,而是用一个数字id代替,解析的时候也是通过这个id去关联键值。具体还是从上面序列化后的字符串s下手,它是一个二进制的字符串,我用一个特殊的语法来表示这8个字节:

  1. 0010 0010, 0000 0100, s, h, a, w, 0010 1000, 0000 1111

可以看到,是通过 Key Value Key Value 的形式排列的,Key 与 message 中定义的 Field 的取值及类型有关。定义如下 (field_number << 3) | wire_type,wire_type 的取值跟类型有关,可以查表获得:

Type Meaning Used For
0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 64-bit fixed64, sfixed64, double
2 Length-delimited string, bytes, embedded messages, packed repeated fields
5 32-bit fixed32, sfixed32, float

有了这个公式就知道 GPB 传输的 Key 是怎么算出来的了:
第一个字节: 4 << 3 | 2 = 34 (0010 0010)
第二个字节: 4 (后面的字节长度)
第七个字节: 5 << 3 | 0 = 40 (0010 1000,Key)
第八个字节: 15 (0000 1111,Value)

假设我现在要扩展 PERSON 对象,加一个email属性扩展 optional string email = 6;,各位有兴趣可以看看这个输出的合理性:

  1. 0010 0010, 0000 0100, s, h, a, w, 0010 1000, 0000 1111, 0011 1010, 1111 0000, r, i, c, h, s, h, a, w, @, 1, 2, 6, ., c, o, m

另外,GPB还使用了 Varint 来表示数字,它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。对于可选的 Field,如果消息中不存在该 field,那么在最终的 Message Buffer 中就没有该 field,这些特性都有助于节约消息本身的大小。

通过上面的例子,应该可以看到,GPB在网络、IO上十分高效,对其跨平台、可扩展性等方面会加深一点认识了。我想比较适合它的场景应该是:

同时也应该看到一些缺点:

参考

2016-08-09 喜欢

Copyright © 2015-2018 转载请注明出处

回到顶部 ↑