gRPC快速入门(二)——Protobuf序列化原理解析
一、Protobuf序列化原理简介
1、序列化
序列化是将数据结构或对象转换成二进制字节流的过程。
Protobuf对于不同的字段类型采用不同的编码方式和数据存储方式对消息字段进行序列化,以确保得到高效紧凑的数据压缩。
Protobuf序列化过程如下:
(1)判断每个字段是否有设置值,有值才进行编码。
(2)根据字段标识号与数据类型将字段值通过不同的编码方式进行编码。
(3)将编码后的数据块按照字段类型采用不同的数据存储方式封装成二进制数据流。
2、反序列化
反序列化是将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程。
Protobuf反序列化过程如下:
(1)调用消息类的parseFrom(input)解析从输入流读入的二进制字节数据流。
(2)将解析出来的数据按照指定的格式读取到Java、C++、Phyton对应的结构类型中。
二、Protobuf编码方式
1、Varint编码
Varint编码是一种变长的编码方式,编码原理是用字节表示数字,值越小的数字,使用越少的字节数表示。因此,可以通过减少表示数字的字节数进行数据压缩。
对int32类型的数字,一般需要4个字节表示。如果采用Varint编码,对于很小的int32类型数字,则可以用1个字节来表示;虽然大的数字会需要5个字节来表示,但大多数情况下,消息都不会有很大的数字,所以采用Varint编码方式总是可以用更少的字节数来表示数字。
Varint编码后每个字节的最高位都有特殊含义:
A、如果是1,表示后续的字节也是数字的一部分。
B、如果是0,表示本字节是最后一个字节,且剩余7位都用来表示数字。
当使用Varint解码时时,只要读取到最高位为0的字节时,表示本字节是一个值经Varint编码后得到的字节流的最后一个字节。
在计算机内,负数一般会被表示为很大的整数 ,因为计算机定义负数的符号位为数字的最高位,如果采用Varint编码方式表示一个负数,那么一定需要5个byte(因为负数的最高位是1,会被当做很大的整数处理)
Protobuf定义了sint32 / sint64类型表示负数,通过先采用Zigzag编码(将有符号数转换成无符号数),再采用Varint编码,从而用于减少编码后的字节数。
对于一个int32类型的值300的Varint编码如下:
300的二进制编码为:100101100(256+32+8+4)
从字节流末尾取出7bit并在最高位增加1构成一个字节:[1]010 1100
从字节流末尾取出7bit并在最高位增加1构成一个字节,如果是最后一个字节增加0:[0]0000010
两字节为:[0]0000010 [1]010 1100
转换为小端模式:10101100 00000010
编码结果:1010 1100 0000 0010
2、Zigzag编码
Zigazg编码是一种变长的编码方式,其编码原理是使用无符号数来表示有符号数字,使得绝对值小的数字都可以采用较少字节来表示,特别对表示负数的数