当前位置: 首页 > news >正文

prtobuf的原理

好处:

在protobuf里定义完Message,自动生成C++的一系列属性字段,处理字段的方法,处理类的方法(序列化/反序列化),后面两个都是吃力不讨好(不算难,但比较麻烦)

它有一个非常棒的特性,即“向后”兼容性好,人们不必破坏已部署的、依靠“老”数据格式的程序就可以对数据结构进行升级。这样可以不必担心因为消息结构的改变而造成的大规模的代码重构或者迁移的问题。因为添加新的消息中的 field 并不会引起已经发布的程序的任何改变。

Protbuf 与 XML 相比也有不足之处。它功能简单,无法用来表示复杂的概念。

XML 已经成为多种行业标准的编写工具,Protobuf 只是 Google 公司内部使用的工具,在通用性上还差很多。

由于文本并不适合用来描述数据结构,所以 Protobuf 也不适合用来对基于文本的标记文档(建模。

另外,由于 XML 具有某种程度上的自解释性,它可以被人直接读取编辑,在这一点上 Protobuf 不行,它以二进制的方式存储,除非你有 .proto 定义,否则你没法直接读出 Protobuf 的任何内容

为什么protobuf序列化后的数据更小?

protobuf信息的表示非常紧凑,这意味着消息的体积减少,自然需要更少的资源

比如网络上传输的字节数,需要的 IO 更少等,从而提高性能。

原因

采用Varint 编码:一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。

Varint 编码

 int32 类型的数字,一般需要 4 个 byte 来表示。但是采用 Varint,对于很小的 int32 类型的数字,则可以用 1 个 byte 来表示。当然凡事都有好的也有不好的一面,采用 Varint 表示法,大的数字则需要 5 个 byte 来表示。从统计的角度来说,一般不会所有的消息中的数字都是大数,因此大多数情况下,采用 Varint 后,可以用更少的字节数来表示数字信息

Varint 中的每个bit的最高位 bit 有特殊的含义,如果该位为 1,表示后续的bit 也是该数字的一部分,如果该位为 0,则结束。其他的 7 个 bit 都用来表示数字。因此小于 128 的数字都可以用一个 byte 表示。大于 128 的数字,比如 300,会用两个字节来表示:1010 1100 0000 0010。下图演示了 Google Protocol Buffer 如何解析两个 bytes

这样来看,小于128的剩了三个字节,还是挺多的

消息经过序列化后会成为一个二进制数据流。如下图所示

采用这种 TLV结构无需使用分隔符来分割不同的 Field。

对于可选的 Field,如果消息中不存在该 field,那么在最终的 Message Buffer 中就没有该 field(可拔插的感觉),这些特性都有助于节约消息本身的大小。

ZigZag 编码

负数protobuf采用ZigZag 编码绝对值小的数字,无论正负都可以采用较少的 byte 来表示,充分利用了 Varint 这种技术 

Encoding过程

采用了 TLV(tag-length-value) 编码格式。

  • 每个字段都有唯一的 tag 值,它是字段的唯一标识
  • length 表示 value 数据的长度,length 不是必须的,对于固定长度的 value,是没有 length 的
  • value是采用Varint和Zigzag编码后的消息字段的值(一堆数字)

计算公式为tag=field_number<<3 | wire_type, 然后在对其采用 Varint 编码

file_number为之前讲的每个消息的唯一的消息编号

wire_type则是数据类型,是int还是string

TVL都要采取ZigZag 编码或Varint 编码,如果是val是string就采取utf-8

为什么普通的类型int不用length?

因为 Varint 和 Zigzag 编码可以自解析内容的长度,所以可以省略长度项。

TLV 存储简化为了 TV 存储,不需 length 项 

可以自解析内容的长度,也就不用分割符了

怎么就可以自解析长度了?

Varint 中的每个 byte 的最高位 bit 有特殊的含义,如果该位为 1,表示后续的 byte 也是该数字的一部分,如果该位为 0,则结束。

你一个byte一个byte读,10的最高位为0,则下一个byte开始就是数据了

数据从前往后逐个字节读,到最后一个字节的首位为0,数据读取完毕,接下来的就是下一个字段了

最高位的bit有特殊含义+一个bit一个bit读

反序列化的大概过程

  • 从文件首字节开始往后一个字节一个字节读,有个字节首位为0的标志读完tag字段
  • 如果是有length的就和上面一样,一个字节一个字节读,有个字节首位为0的标志读完length字段
  • 没有length的话和上面一样,一个字节一个字节读,有个字节首位为0的标志读完val字段
  • 解析放进C++类中的对象的成员变量中
http://www.xdnf.cn/news/80983.html

相关文章:

  • 2.Spring MVC与WebFlux响应式编程
  • UOS+N 卡 + CUDA 环境下 X86 架构 DeepSeek 基于 vLLM 部署与 Dify 平台搭建指南
  • Nature Communications 面向形状可编程磁性软材料的数据驱动设计方法—基于随机设计探索与神经网络的协同优化框架
  • 30分钟编写十大排序算法完成
  • 施磊老师基于muduo网络库的集群聊天服务器(四)
  • 含锡废水具有显著的回收价值
  • kvm下的ceph主机启动io请求统计
  • AOSP Android14 Launcher3——RecentsView最近任务数据加载
  • Hive学习
  • 【数字图像处理】立体视觉基础(1)
  • 禁止ubuntu自动更新
  • 基于nlohmann/json 实现 从C++对象转换成JSON数据格式
  • c++内存池
  • 调整IntelliJ IDEA中当前文件所在目录的显示位置
  • 可吸收聚合物:医疗科技与绿色未来的交汇点
  • 解决IntelliJ IDEA配置文件(application.properties)中文注释变成乱码的问题
  • linux驱动---视频播放采集架构介绍
  • 2025年数字媒体设计与文化交流国际会议 (DMACE 2025)
  • 【Python进阶】VSCode Python开发完全指南:从环境配置到高效调试
  • 【数据结构和算法】5. 堆栈和队列
  • Android Retrofit原理解析
  • map和set
  • 如何让 vscode jupyter 访问 terminal 的环境变量?
  • 【医学影像 AI】基于 AI 的远程筛查 ROP 效果评价
  • UML-网络媒体教学系统顺序图深度解析
  • Springboot+Vue实现邮箱验证功能(邮箱登录+忘记密码)
  • 嵌入式:ARM公司发展史与核心技术演进
  • AtCoder 第402场初级竞赛 A~E题解
  • 【工具变量】中国服务贸易OECD-进出口相关数据(2005-2023年)
  • 【 React 】重点知识总结 快速上手指南