网上看到一篇很详细的文章,因为涉及到很多图片,所以直接发地址把
protocol buffer序列化过的对象,最终转成二进制流,它的形式是T-L-V的格式,
1. Tag
定义:经过
Protocol Buffer
采用Varint
&Zigzag
编码后 的消息字段 标识号 & 数据类型 的值作用:标识 字段
- 存储了字段的标识号(
field_number
)和 数据类型(wire_type
),即Tag
= 字段数据类型(wire_type
) + 标识号(field_number
) - 占用 一个字节 的长度(如果标识号超过了16,则占用多一个字节的位置)
- 解包时,
Protocol Buffer
根据Tag
将Value
对应于消息中的 字段
- 存储了字段的标识号(
具体使用
1 | // Tag 的具体表达式如下 |
- 实例说明
1 | // 消息对象 |
2. Value
经过 Protocol Buffer
采用Varint
& Zigzag
编码后 的消息字段的值
实例说明
下面通过一个实例进行整个编码过程的说明:
- 消息说明
1 | message Test |
对于String类型,value则直接是其UTF8的字节表示,L则是其长度
对于嵌套类型,则表示有可能是TL-TLV-TLV
还有一种情况,则是packed
修饰的 repeat
字段,可以存储为TL-VVVVV的形式
比较核心和有意思的则是它编码的方法,从效果来看很像huffman coding,思想都是把高频的数据进行压缩,比如
低于256的int值,我们不需要4字节去存储,可以去掉高位的三字节0位,压缩为1字节去存储
Varint
编码:
1 | private void writeVarint32(int n) { |
采用Varint编码,对于高位为0,不足4字节的正数,值越小编码后生成的byte值就越小,但对于负数,由于计算机里面有符号数负数表示为高位1,那么采用Varint编码的时候,则会把4byte的数据转成5byte,因此在数据中有负数的时候,需要先用 Zigzag
编码转一下
Zigzag
编码方式详解
i. 简介
定义:一种变长的编码方式
原理:使用 无符号数 来表示 有符号数字;
作用:使得绝对值小的数字都可以采用较少 字节 来表示;
特别是对 表示负数的数据 能更好地进行数据压缩
b. 原理
- 源码分析
1 | public int int_to_zigzag(int n) |
比如-2,2表示为0000 0010,-2则是1111 1110,按照公式,-2左移一位,得到1111 1100,-2右移31位,得到1111 1111,两者异或,得到0000 0011,十进制是3,这样子,-2就转成了3
对比于XML
的序列化 & 反序列化过程
XML的反序列化过程如下:
- 从文件中读取出字符串
- 将字符串转换为
XML
文档对象结构模型 - 从
XML
文档对象结构模型中读取指定节点的字符串 - 将该字符串转换成指定类型的变量
上述过程非常复杂,其中,将 XML
文件转换为文档对象结构模型的过程通常需要完成词法文法分析等大量消耗 CPU 的复杂计算。
因为序列化 & 反序列化过程简单,所以序列化 & 反序列化过程速度非常快,这也是 Protocol Buffer
效率高的原因
总结
Protocol Buffer
的序列化 & 反序列化简单 & 速度快的原因是:
a. 编码 / 解码 方式简单(只需要简单的数学运算 = 位移等等)
b. 采用 Protocol Buffer 自身的框架代码 和 编译器 共同完成Protocol Buffer
的数据压缩效果好(即序列化后的数据量体积小)的原因是:
a. 采用了独特的编码方式,如Varint
、Zigzag
编码方式等等
b. 采用T - L - V
的数据存储方式:减少了分隔符的使用 & 数据存储得紧凑