Ylisar 发表于 2022-2-21 20:49

正确使用 protobuf 的姿势

B站千万级弹幕通信协议protobuf工程实践

Protobuf 总结

用途

Protobuf 是 google 出品的序列化框架,可跨平台、跨语言使用,扩展性良好。与 XML, JSON 等序列化框架相同,Protobuf 广泛的应用于数据存储,网络传输,RPC 调用等环境。
序列化: 将 数据结构或对象转换成二进制串的过程
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
序列化原理分析

优势

性能方面


[*]体积小:Protobuf 中使用了多种编码(Varint、Zigzag),序列化后,数据大小可缩小 3 倍
[*]传输速度快:带宽相同的情况下,体积小的传输速度更快
[*]序列化速度快:直接把对象和字节数据做转换
使用方面


[*]使用简单,维护成本低:仅需要维护一份 .proto 文件,protoc 编译器支持多种平台代码的生成
[*]向后兼容好:支持字段的增加和删除,笔者认为 json 亦可支持
[*]安全性:protobuf 编码后的数据比 json 编码的数据更难分析
[*]适用性: 不适用于对基于文本的标记文档(如 HTML)建模,但在传输数据量大 & 网络环境不稳定的数据存储、RPC 数据交换的场景下很适用
原理

Protocol Buffer 将消息里的每个字段进行编码后,再利用 T - L - V 方式进行数据存储。



[*]Tag - Length - Value, 标识 - 长度 -字段值存储方式

不需要分隔符就能分割字段,减少分割符使用
采用 Varint & Zigzag 编码方式,存储空间利用率高
没有设置字段值的字段,不需要编码(存储或传输过程中数据是完全不存在的),相应字段在解码的时候才会被设置默认值Protocol Buffer 对于不同数据类型,采用不同的序列化方式(编码方式 & 数据存储方式)

[*]Varint 编码方式
变长编码方式,用字节表示数字,值越小的数字,使用越少的字节数表示,通过减少表示数字的字节数从而进行数据压缩。

[*]对于 int32 类型的数字,一般需要 4 个字节表示,若使用 Varint 编码,对于很小的 int32 类型数字,可以用 1 个字节表示,但很大的数字需要 5 个字节表示。
[*]在计算机内,负数的符号位为数字的最高位,会被计算机理解为很大的整数,一定需要 5 个 byte,所以 protobuf 中又引入了 Zigzag 编码。


[*]Zigzag 编码方式
变长编码方式,使用无符号数来表示有符号数字,使得绝对值小的数字都可以采用比较少子节来表示。特别是对表示负数的数据能更好地进行数据压缩Ptotocol Buffer 对于数据编码方式和T - L -V 数据存储方式,使得序列化后体积更小
使用建议


[*]字段标识号(Field_Number)尽量使用 1-15,且不要跳动使用
tag 里的 Field_Number 字段是需要占用字节空间的。如果 Field_Number 大于 16, Field_Number 的编码就会占用 2 个字节, 那么 Tag 在编码时也就会占用更多的字节。

[*]若需要使用的字段值出现负数,优先使用 sint32/sint64
采用 sint32/ sint64 数据类型表示负数时,会优先使用 Zigzag 编码再采用 Varint 编码,更加有效的压缩数据。测试结果分析

在分析过原理之后,深入思考下 protobuf 是不是在任何使用场景下都是合适的?可以考虑如下几种场景:

[*]如果字段大部分都是字符串,占到决定性因素应该是字符串拷贝速度,而不是解析速度。
[*]影响解析速度的决定性因素是分支的数量,上述建议字段标识号不要超过 15。因为分支的存在,解析仍然是一个串行的过程。
[*]理论和实践并不一定完全保存一致。Protobuf 是一个理论上更快的格式,但是实现它的库并不一定就更快。而是取决于优化做得好不好,比如是否有不必要内存分配或者重复读取。
网上整理的测评结果

整理的是 Jackson 和 Protobuf 的性能对比


Jackson 是 Java 程序中用的最多的 JSON 解析器,benchmark 中开启了 AfterBurner 的加速特性(笔者不懂 Java 不知道这是用来做什么的)Proto3 区别于 Proto2 的使用

在第一行非空非注释行,必须写:syntax = "proto3";
字段规则移除 「required」,并把 「optional」改为 「singular」
「repeated」字段默认使用 paced 编码
移除 default 选项
枚举类型的第一个字段必须要为 0
移除对扩展的支持,新增 Any 类型, Any 类型是用来替代 proto2 中的扩展的
增加了 JSON 映射特性
原文地址C/C++linux服务器开发 学习视频课程
Linux服务器开发/架构师面试题、学习资料、教学视频和学习路线图(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享有需要的可以自行添加 学习交流群
页: [1]
查看完整版本: 正确使用 protobuf 的姿势