[转载]C++序列化框架介绍和对比

本文介绍了多种序列化工具,如Google Protocol Buffers、Nanopb、Flatbuffers等。详细阐述了Google Protocol Buffers的编码原理、优缺点及优化方式,还介绍了其他工具的特点、设计原理和使用样例,并对各种序列化工具进行了性能评测,指出Protobuf在Deno中使用的不足。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Google Protocol Buffers

Protocol buffers 是一种语言中立,平台无关,可扩展的序列化数据的格式,可用于通信协议,数据存储等。
Protocol buffers 在序列化数据方面,它是灵活的,高效的。相比于 XML 来说,Protocol buffers 更加小巧,更加快速,更加简单。一旦定义了要处理的数据的数据结构之后,就可以利用 Protocol buffers 的代码生成工具生成相关的代码。甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。
Protocol buffers 很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。--《 深入理解Protobuf原理与工程实践
大家可能会觉得Google发明Protocol buffers是为了解决序列化速度的,其实真实的原因并不是这样的。Protocol buffers最先开始是Google为了解决服务器端新旧协议(高低版本)兼容性问题,名字也很体贴, “协议缓冲区”。只不过后期慢慢发展成用于传输数据。--《 深入理解Protobuf原理与工程实践
Why the name "Protocol Buffers"?  The name originates from the early days of the format, before we had the protocol buffer compiler to generate classes for us. At the time, there was a class called ProtocolBuffer which actually acted as a buffer for an individual method. Users would add tag/value pairs to this buffer individually by calling methods like AddValue(tag, value). The raw bytes were stored in a buffer which could then be written out once the message had been constructed.  Since that time, the "buffers" part of the name has lost its meaning, but it is still the name we use. Today, people usually use the term "protocol message" to refer to a message in an abstract sense, "protocol buffer" to refer to a serialized copy of a message, and "protocol message object" to refer to an in-memory object representing the parsed message.

编码原理
Protobuf高效的秘密在于它的编码格式,它采用了T-(L)-V(Tag-Length-Value)编码格式。每个字段都有唯一的 tag 值,它是字段的唯一标识。length 表示 value 数据的长度,length 不是必须的,对于固定长度的 value,是没有 length 的。value 是数据本身的内容。


对于 tag 值,它有 field_number 和 wire_type 两部分组成。field_number 就是在前面的 message 中我们给每个字段的编号,wire_type 表示类型,是固定长度还是变长的。 wire_type 当前有0到5一共6个值,所以用3个 bit 就可以表示这6个值。tag 结构如下图。


wire_type 值如下表, 其中3和4已经废弃,我们只需要关心剩下的4种。对于 Varint 编码数据,不需要存储字节长度 length。这种情况下,TLV 编码格式退化成 TV 编码。对于64-bit和32-bit也不需要 length,因为type值已经表明了长度是8字节还是4字节。

wire_type 编码方法 编码长度 存储方式 数据类型
0 Varint 变长 T-V int32 int64 uint32 uint64 bool enum
0 Zigzag+Varint 变长 T-V sint32 sint64
1 64-bit 固定8字节 T-V fixed64 sfixed64 double
2 length-delimi 变长 T-L-V string bytes packed repeated fields embedded
3 start group 已废弃 已废弃
4 end group 已废弃 已废弃
5 32-bit 固定4字节 T-V fixed32 sfixed32 float


因为 Varint 和 Zigzag 编码可以自解析内容的长度,所以可以省略长度项。TLV 存储简化为了 TV 存储,不需 length 项。


嵌套类型编码
嵌套消息就是value又是一个字段消息,外层消息存储采用 TLV 存储,它的 value 又是一个 TLV 存储。整个编码结构如下图所示。


优缺点
优点:

  1. 效率高

从序列化后的数据体积角度,与XML、JSON这类文本协议相比,ProtoBuf通过T-(L)-V(Tag-Length-Value)方式编码,不需要", {, }, :等分隔符来结构化信息,同时在编码层面使用varint压缩,所以描述同样的信息,Protobuf序列化后的体积要小很多,在网络中传输消耗的网络流量更少,进而对于网络资源紧张、性能要求非常高的场景,Protobuf协议是不错的选择。

2. 支持跨平台、多语言

ProtoBuf是平台无关的,无论是Android与PC,还是C#与Java都可以利用Protobuf进行无障碍通讯。
proto3支持C++, Java, Python, Go, Ruby, Objective-C, C#。

3. 扩展性、兼容性好

具有向后兼容的特性,更新数据结构以后,老版本依旧可以兼容,这也是Protobuf诞生之初被寄予解决的问题。因为编译器对不识别的新增字段会跳过不处理。

4. 使用简单

Protobuf 提供了一套编译工具,可以自动生成序列化、反序列化的样板代码,这样开发者只要关注业务数据idl,简化了编码解码工作以及多语言交互的复杂度。

5. 具备一定的加密性

由于没有idl文件无法解析二进制数据流,Protobuf在一定程度上可以保护数据,提升核心数据被破解的门槛,降低核心数据被盗爬的风险。


缺点:

  1. 可读性差,缺乏自描述

XML,JSON是自描述的,而Protobuf则不是。
Protobuf是二进制协议,编码后的数据可读性差,如果没有idl文件,就无法理解二进制数据流,对调试不友好。

启用GZip压缩功能

但凡对带宽敏感的程序,无论采用何种编码方式都应该进行数据压缩。

#include <google/protobuf/io/gzip_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>

std::string output;  // 压缩序列化 

google::protobuf::io::GzipOutputStream::Options options; 
options.format &
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值