protobuf特性剖析

这篇文章主要剖一下protolbuf几大特性的底层实现原理,不对基础语法做过多的解释

varint可变长度压缩: 在传输较小的数字,可达到压缩的效果,具体见下图:

小于等于127的数字存储模型:
在这里插入图片描述
大于等于128的数字存储模型:
在这里插入图片描述

key字段名压缩:一般rpc协议设计过程中,我们都需要把字段名(key)和字段值(value)发送给对方,但是pb却采用了一个几位巧妙的方法:通过第一个字节来描述字段类型和字段在结构中的顺序来达到数据封装和解封的效果,具体算法如下:
在这里插入图片描述
如图所示,pb把字段的类型划分成了0~5号,序列化过程中,用第一个字节中的三位存储类型信息,其余信息存储标号的顺序,这样就通过字段类型和字段标号来达到了传统方案的效果,并且大大节省了因字段名占用的空间:
注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。

[1,15]之内的标识号在编码内存排布:
在这里插入图片描述
注释:以上截图就是一个id字段标号=10的int32类型字段名的压缩过程,“tag(字段标号)<<3(左移三位)|ware_type(字段类型)”,这样就通过一个字节实现了字段和类型的压缩。

[16,2047]之内的标识号在编码内存排布:
在这里插入图片描述
注释:以上截图就是一个id字段标号=16的int32类型字段名的压缩过程,“tag(字段标号)<<3(左移三位)|ware_type(字段类型)”,这样就通过两个字节实现了字段和类型的压缩。

packed修饰字段压缩:
正常情况下,一个数组的内存排布肯定是tag-value tag-value tag-value…的模型:
在这里插入图片描述
在这里插入图片描述
问题:若以传统的多个 T - V对存储(不带packed=true),则会导致Tag的冗余,即相同的Tag存储多次;

在这里插入图片描述

负数压缩:
Varint 编码方式的不足
背景:在计算机内,负数一般会被表示为很大的整数
因为计算机定义负数的符号位为数字的最高位
问题:如果采用 Varint编码方式 表示一个负数,那么一定需要 5 个 byte(因为负数的最高位是1,会被当做很大的整数去处理)
解决方案: Protocol Buffer 定义了 sint32 / sint64 类型表示负数,通过先采用 Zigzag 编码(将 有符号数 转换成 无符号数),再采用 Varint编码,从而用于减少编码后的字节数
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
通过以上几种压缩方法,大大降低了空间占用,减少网络带宽压力。

扩展性、兼容性:
通过tag标号的方式,很好的解决了版本协议新增的新老版本兼容性问题,这里就不在过多介绍了。

详细参考:https://cloud.tencent.com/developer/article/1394349

使用方法:

安装:https://github.com/protocolbuffers/protobuf下载源码
tar zxvf protobuf-cpp-3.8.0.tar.gz
cd protobuf-cpp-3.8.0/
./configure
sudo make install
显示版本信息:protoc --version

根据proto文件生成.cc和.h文件:
在这里插入图片描述

引用生成的.h和.cc:
g++ -o mode_name code_test.cpp xx.cc -lprotobuf -lpthread

基本语法参考翻译:https://blog.csdn.net/u011518120/article/details/54604615?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allfirst_rank_v2~rank_v25-1-54604615.nonecase&utm_term=proto%E7%9A%84oneof%E4%BB%80%E4%B9%88%E4%BD%9C%E7%94%A8&spm=1000.2123.3001.4430

猜你喜欢

转载自blog.csdn.net/wangrenhaioylj/article/details/109178376