protobuf和grpc
作者:互联网
一、grpc安装
在安装之前确保已经安装好了c/c++的编译环境(指令:sudo apt install -y build-essential autoconf libtool pkg-config)以及cmake、openssl等工具。
(1)下载grpc
git clone https://github.com/grpc/grpc.git cd grpc git submodule update --init
(2)编译安装protobuf
cd third_party/protobuf/ git submodule update --init --recursive ./autogen.sh ./configure make -j8 sudo make install sudo ldconfig
(3)编译安装grpc
cd grpc mkdir -p cmake/build cd cmake/build cmake ../.. make -j8 sudo make install
(4)编译example
cd grpc/examples/cpp/helloworld cmake ../.. make -j8 #启动服务端 ./greeter_server #启动客户端 ./greeter_client
二、protobuf编码原理
(1)Varints编码
Varints编码使用每个字节的最高有效位做为标志位,而剩余的7位以二进制补码的形式来存储数字值本身,当最高有效位为1时,代表其后面还跟有字节,当最高有效位为0时,代表已经是该数字的最后一子节,在protobuf中,使用的是Base128 Varints编码,之所以这么称呼,是因为使用7bit来存储数字,在protobuf中,Base128 Varints采用的是小端序,即数字的低位存放在高位地址,
十进制:127 二进制:111 1111 Varints:0111 1111 (最高位0,表示没有后续byte) ----> 16进制: 0x7f 十进制:128 二进制(7bit分隔): 1|000 0000 Varints: ① 取最低的7bit: 1000 0000 (最高位1,表示有后续byte)----> 16进制: 0x80 ② 剩余的1bit,不足补0: 0000 0001 (最高位0,表示没有后续byte)----> 16进制: 0x01 ③ 小端存储:0x80,0x01 十进制:16385 二进制(7bit分隔):1|000 0000|000 0001 Varints: ① 取最低的7bit: 1000 0001 (最高位1,表示有后续byte)----> 16进制: 0x81 ② 中间7bit: 1000 0000 (最高位1,表示有后续byte)----> 16进制: 0x80 ③ 剩余的1bit: 0000 0001 (最高位0,表示没有后续byte)----> 16进制: 0x01 ③ 小端存储:0x81,0x80,0x01
(2)Zigzag编码
Zigzag 编码的大致思想是首先对负数做一次变换, 将其映射为一个正数, 变换以后便可以使用 Varints 编码进行压缩, 这里关键的一点在于变换的算法, 首先算法必须是可逆的, 即可以根据变换后的值计算出原始值, 否则就无法解码, 同时要求变换算法要尽可能简单, 以避免影响 Protobuf 编码、解码的速度;则 Zigzag 编码的计算方式为:
(n << 1) ^ (n >> 31)
左边是逻辑移位, 右边是算术移位, 右边的含义实际是得到⼀个全 1 (对于负数) 或全 0(对于正数)的⽐特序列。
假设数字为 -5, 其在内存中的形式为:
⾸先对其进⾏⼀次逻辑左移, 移位后空出的⽐特位由 0 填充。
然后对原数字进⾏ 15 次算术右移, 得到 16 位全为原符号位(即 1)的数字。
然后对逻辑移位和算术移位的结果按位异或, 便得到最终的 Zigzag 编码。
可以看到,对于负数使用Zigzag编码以后,其高位的1全部变为0,这样便可以使用Varints编码进一步压缩,再来看正数的情况,对于16位的正数5,其在内存中的存储形式为:
按照与负数相同的处理方法,可以得到其Zigzag编码为:
从上⾯的结果来看, ⽆论是正数还是负数, 经过 Zigzag 编码以后, 数字⾼位都是 0, 这样以来,便可以进⼀步使⽤ Varints 编码进⾏数据压缩,即 Zigzag 编码在 Protobuf 中并不单独使⽤, ⽽是配合Varints 编码共同来进⾏数据压缩。
注意事项:
(a)当字段可能为负数时, 我们应使⽤ sint32 或 sint64, 这样Protobuf 会按照 Zigzag 编码将数据变换后再采⽤ Varints 编码进⾏压缩,从⽽缩短数据的⼆进制位数;
(b)protobuf不是完全自描述的信息格式,接收端需要有相应的解码器(即proto定义)才可以解析数据格式,序列化后的protobuf数据不携带字段名,只使用字段编号来标识一个字段,因此更改proto的字段名不会影响数据的解析,字段编号会被编码进二进制的消息结构中,因此尽可能地使用小的编号;
(c)protobuf是一种紧密的消息结构,编码后字段之间没有间隔,每个字段头由两部分组成:字段编号和wire type,字段头可确定数据段的长度,因此其字段之前无需加间隔,也无需引入特定的数据来标记字段末尾,因此protobuf的编码长度短,传输效率高;
(d)如果添加新字段,客户端使用了新的代码,服务端依然可以使用旧的二进制在解析,只是会简单地忽略新添加字段。
三、protobuf的使用
(1)数值类型
一个标量消息字段可以含有一个如下的类型
(2)定义一个消息类型:
syntax = "proto3"; message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; }
在消息定义中,每个字段都有唯一的一个数字标识符,这个标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用不能够再改变,范围[1,2^29-1]
(3)枚举的使用
message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; enum Corpus { UNIVERSAL = 0; //从0开始 WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } Corpus corpus = 4; }
(4)服务定义
如果将消息用在rpc远程方法调用,可以在proto文件中定义一个rpc服务接口,该方法接收参数helloRequest,返回一个helloResponse。
service myService{ rpc Hello(helloRequest) returns (helloResponse) ; }
标签:编码,protobuf,Varints,16,grpc,Zigzag 来源: https://www.cnblogs.com/juju-go/p/16406109.html