protocol buffers原理与使用

一、简介

protocol buffers是google推出的一种数据序列化格式,简称protobuf。

具有以下优点

  • 支持多种编程语言
  • 序列化数据体积小
  • 反序列化速度快
  • 序列化和反序列化代码自动生成

同时,也有以下缺点:

  •  可读性差,缺乏自描述性

在项目 thrift-protobuf-compare中,对各种序列化方式做了全方位的对比,对比结果中,protobuf各项都处于第5左右(共21个),综合性能较好。

下图(左JSON,右Protobuf)是同样的一段数据,用json和protobuf分别描述(仅表示描述方式,并不是最终生成的序列化数据)。可以看出,protobuf是把json中的key去掉了,用数字代替key,从而实现了减小了序列化后的数据大小。而protobuf反序列化过程中,无需做字符串解析,所以速度也很快,综合性能优于json很多。

protocol buffers原理与使用_第1张图片protocol buffers原理与使用_第2张图片

二、使用步骤

protobuf使用,主要分为以下步骤:

1. 定义消息格式,编写.proto文件

2. 选择合适的protobuf实现框架,对.proto文件进行编译,生成对应的源代码文件

3. 在代码中调用生成的源代码文件,完成序列化和反序列化功能

三、在Android中使用

目前在Android种有两种实现框架,google-protobuf和square-wire。

1. 使用google-protobuf框架

以后补充

2. 使用square-wire框架

以后补充

 

四、原理

1. 消息格式

如下图所示,protobuf数据是连续的key-value组成,每个key-value对表示一个字段,value可以是基础类型,也可以是一个子消息。

其中,key表示了该字段数据类型,字段id信息,而value则是该字段的原始数据。若字段类型是string/bytes/子message(长度不固定),则在key和value之间还有一个值表示该字段的数据长度,如下图所示:

key值的计算方式为:key=(id<<3)|type,其中id是在消息定义时的字段id,而type表示数据类型,取值范围0-5,如下表所示:

protocol buffers原理与使用_第3张图片

例:假如proto文件定义如下,其中消息取值为code=10,msg="abc",尝试计算序列化后的消息数据。

message Response {
    required int32 code = 1;
    required string msg = 2;
}

答:

a. 消息的最终结构为key1-value1-key2-length2-value2;

b. 其中,key1=(id<<3)|type = (1<<3)|0=0x08,value1=0x0a,key2=(id<<3)|type=(2<<3)|2=0x12,length2=0x03,value2=0x616263;

c. 因此,最终的消息数据为:0x080a1203616263.

2. varint编码

在java中,int用4字节表示,long用8字节表示,而在实际场景中,一般我们不会使用到很大的数,因此使用4/8字节表示数值会有冗余。为了进一步减小序列化后的数据大小,protobuf引入了varint编码,解决数值表示过程中的冗余问题。

在protobuf中,key值/length/int32/int64/uint32/uint64使用了varint编码表示。

varint编码原理如下图所示,每个字节首位表示后面是否还有字节,而后7位表示数据。图中所示的数据,用二进制表示实际上是00101000100111(protobuf采用小端模式),即2599。

protocol buffers原理与使用_第4张图片


3. zigzag编码

由于负数在计算机中用补码表示,首位永远是1,无法使用varint编码进行压缩。为了解决此问题,protobuf引入了zigzag编码,目前,sint32和sint64类型的数据使用zigzag编码。

zigzag编码原理是,首先按照下表中的规律,将数据全部转化成正数,然后再用varint进行编码。

例:-3(int类型)用zigzag编码处理后,结果为0x05,仅需1个字节;而若不用zigzag,在内存中表示为0xFFFFFFFD,需要4个字节;若直接使用varint编码,则是0xFEFFFFFF0F,需要5个字节。

protocol buffers原理与使用_第5张图片

 

你可能感兴趣的:(安卓)