专栏首页张善友的专栏Protocol Buffers的应用

Protocol Buffers的应用

1. Protocol Buffers的介绍

Protocol buffers are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages – Java, C++, or Python. You can even update your data structure without breaking deployed programs that are compiled against the “old” format.(摘自Protocol Buffers官网

protocol buffers是google提供的一种将结构化数据进行序列化和反序列化的方法,其优点是语言中立,平台中立,可扩展性好,目前在google内部大量用于数据存储,通讯协议等方面。Protocol Buffers在功能上类似XML,但是序列化后的数据更小,解析更快,使用上更简单。用户只要按照proto语法在.proto文件中定义好数据的结构,就可以使用Protocol Buffers提供的工具(protoc)自动生成处理数据的代码,使用这些代码就能在程序中方便的通过各种数据流读写数据。PB目前支持Java, C++和Python3种语言。另外,Protocol Buffers还提供了很好的向后兼容,即旧版本的程序可以正常处理新版本的数据,新版本的程序也能正常处理旧版本的数据。

Protocol Buffers具有以下特点:

  1. 平台无关、语言无关
  2. 高性能 比XML块20-100倍
  3. 体积小 比XML小3-10倍
  4. 使用简单
  5. 兼容性好

2、message的编码特点

Protocol Buffers 之所以解析速度快、所占体积小,很大程度上是由它序列化的编码特点来决定的。

2.1 Base 128 Varints

Protocol Buffers采用了Base 128 Varints来变长编码整数:

  1. 变长编码的整数,它可能包含多个byte,对于每个byte的8位,其中后7位表示数值,最高的一位表示是否还有还有另一个byte,0表示没有,1表示有;
  2. 越前面的byte表示数值的低位,越后面的byte表示数值的高位;

例子: 300 varints 编码为:1010 1100 0000 0010 解释如下: 300的2进制编码为:0001 0010 1100 按照刚才的规则,高低位颠倒,截取最后的7为放在第一个byte,则第一byte为1010 1100(其中最高位1表示,后续还有byte);接着剩下的内容放到第二个byte,为0000 0010(其中最高位0表示,后续无byte,这个数到这里截止了)。 于是,合在一起为 1010 1100 0000 0010;

2.2 Key-Value

如前所述,Protocol Buffers的message是一系列的key-value对,在二进制数据中,使用varints数字(包含了别名以及属性类型信息)来作为key,进而通过由PB编译器生成的代码来构造以及解析数据。 Protocol Buffers将 key编码成下面的结构: X YYYY ZZZ 其中:最高位X表示是否还有后续的byte来编码数字别名;YYYY用于编码别名,定义了多余16个属性,则需要用到额外的byte,所以出现频率高的字段应当取1-16的别名);ZZZ表示这个字段的类型,PB支持的属性的对应规则如下表:

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

3

Start group

groups (deprecated)

4

End group

groups (deprecated)

5

32-bit

fixed32, sfixed32, floa

表2:PB 属性对应规则 例子: required int32 a=1; 在应用中给a赋值150 ,序列化后08 96 01

  • 08代表的是key 0 0001 000, 最高位为0,表示这个key为一个byte,中间四位表示a的数字别名,最后三位表示a的属性类型;
  • 96 01代表的是value,二进制为:1001 0110 0000 0001 → 001 0110 000 0001(去掉最高位) → 22 + 1*2^7 = 150

2.3 Zig-Zag

采用varints的方式编码有符号的整数,效率比较差,因为负数的最高位是1,这样就导致了情况类似于编码一个很大的数。

为了解决这个问题,Protocol Buffers定义了sint32/sint64属性,他们采用了“之字形”(ZigZag)编码的方式,将负数编码成正数,交替进行。看了下表就很好理解了:

Signed Original

Encoded As

0

0

-1

1

1

2

-2

3

2147483647

4294967294

2147483648

4294967295

表3:Zig-Zag编码规则

利用这个方式,可以有效地节省存储空间,也能提高解析效率。了解了以上内容,对于其他数据类型的编码,也是很好理解的,大家可以参考官方文档,这里不做详述。

3.为什么不用XML?

ProtocolBuffer拥有多项比XML更高级的串行化结构数据的特性,ProtocolBuffer:

· 更简单

· 小3-10倍

· 快20-100倍

· 更少的歧义

· 可以方便的生成数据存取类

例如,让我们看看如何在XML中建模Person的name和email字段:

<person>   
<name>John Doe</name>    
<email>jdoe@example.com</email>    
</person>

对应的ProtocolBuffer报文则如下:

#ProtocolBuffer的文本表示 #这不是正常时使用的二进制数据

person {    
name: "John Doe"    
email: "jdoe@example.com"    
}

当这个报文编码到ProtocolBuffer的二进制格式( http://code.google.com/apis/protocolbuffers/docs/encoding.html )时(上面的文本仅用于调试和编辑),它只需要28字节和100-200ns的解析时间。而XML的版本需要69字节(除去空白)和5000-10000ns的解析时间。

当然,操作ProtocolBuffer也很简单:

cout << "Name: " << person.name() << endl; cout << "E-mail: " << person.email() << endl;

而XML的你需要:

cout << "Name: " << person.getElementsByTagName("name")->item(0)->innerText() << endl; cout << "E-mail: " << person.getElementsByTagName("email")->item(0)->innerText() << end;

当然,ProtocolBuffer并不是在任何时候都比XML更合适,例如ProtocolBuffer无法对一个基于标记文本的文档建模,因为你根本没法方便的在文本中插入结构。另外,XML是便于人类阅读和编辑的,而ProtocolBuffer则不是。还有XML是自解释的,而 ProtocolBuffer仅在你拥有报文格式定义的 .proto 文件时才有意义。

相关文章:

.net自带二进制序列化,XML序列化和ProtoBuf序列化的压缩对比

WCF服务上应用protobuf

玩转Protocol Buffers

Beetle使用Protobuf.net进行对象序列化传输

Google Protocol Buffer 的使用和原理

Protocol Buffers and WCF http://blogs.msdn.com/b/dmetzgar/archive/2011/03/29/protocol-buffers-and-wcf.aspx

Protobuf-net: the unofficial manual http://www.codeproject.com/Articles/642677/Protobuf-net-the-unofficial-manual

Working with Protobuf WCF Services http://www.drdobbs.com/windows/working-with-protobuf-wcf-services/240159282?pgno=1

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java Spring mvc 操作 Redis 及 Redis 集群

     本文原创,转载请注明:http://www.cnblogs.com/fengzheng/p/5941953.html 关于 Redis 集群搭建可以参考我的另...

    古时的风筝
  • 用java开发微信公众号:公众号接入和access_token管理(二)

    上一篇说了微信开发的准备工作,准备工作完成之后,就要开始步入正题了。其实微信公众号开发,说白了,就是要构造和发送http或https的请求组成,并根据请求的返回...

    古时的风筝
  • 笔记:Binder通信机制

    TODO: 待修正 Binder简介 Binder是android系统中实现的一种高效的IPC机制,平常接触到的各种XxxManager,以及绑定Service...

    用户1172465
  • 用java开发微信公众号:测试公众号与本地测试环境搭建(一)

    俗话说,工欲善其事,必先利其器。要做微信公众号开发,两样东西不可少,那就是要有一个用来测试的公众号,还有一个用来调式代码的开发环境。 测试公众号 微信公众号有订...

    古时的风筝
  • 用java开发微信公众号:接收和被动回复普通消息(三)

    上篇说完了如何接入微信公众号,本文说一下微信公众号的最基本功能:普通消息的接收和回复。说到普通消息,那么什么是微信公众号所定义的普通消息呢,微信开发者文档中提到...

    古时的风筝
  • Java中InetAddress和InetSocketAddress的区别

    在Java中InetAddress和InetSocketAddress看起来很相似,用来描述IP地址和主机名称。当然,它们也支持使用常规方法来检查地址:回环地址...

    用户1161110
  • 模板的简单介绍与使用

    什么是模板? 模板(template)指c++中的函数模板与类模板,大体对应于C#和Java众的泛型的概念。目前,模板已经成为C++的泛型编程中不可缺少的一部分...

    古时的风筝
  • BTrace : Java 线上问题排查神器

    BTrace 是什么 BTrace 是检查和解决线上的问题的杀器,BTrace 可以通过编写脚本的方式,获取程序执行过程中的一切信息,并且,注意了,不用重启服务...

    古时的风筝
  • 札记:Java异常处理

    异常概述 程序在运行中总会面临一些“意外”情况,良好的代码需要对它们进行预防和处理。大致来说,这些意外情况分三类: 交互输入 用户以非预期的方式使用程序,比如...

    用户1172465
  • 零基础如何成功进入Java编程领域呢?

    关于书籍 新人们常常会说我看了多少多少的书,看过某某人写的书,仿佛书看的多懂得也就多了。 其实不然,很多新人在面试的时候夸夸其谈,说啥啥都知道一点,到真正做的时...

    企鹅号小编

扫码关注云+社区

领取腾讯云代金券