C++序列化工具最佳实践

C++序列化工具最佳实践

序列化概述当两个服务在进行通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以字节序列的形式在络上发送。发送方需要把这个对象转换为字节序列,才能在络上发送;接收方需要把字节序列再恢复为对象。

当服务上线后,将领域对象以字节序列的方式存储在分布式数据库中。当该服务突然宕机后,其上的既有业务迁移到了其他同类服务实例上,这时需要从数据库中获取字节序列反构领域对象,使得业务不中断。

这个把对象转换为字节序列的过程被称为“序列化”(serialization),而它的逆过程则被称为“反序列化”(deserialization)。这两个过程结合起来,可以在异构系统中轻松地存储和传输数据。

两种用途:

把对象的字节序列保存在文件或数据库中;

在络上传送对象的字节序列。

必须序列化吗?

是的,核心问题是数据版本的前后项兼容,有了这个约束,就必须将对象序列化。

其他问题比如异构系统,虽然不是核心问题,但是序列化使得处理更加灵活。

C++序列化工具比较对于通信系统,大多都是C/C++开发的,而C/C++语言没有反射机制,所以对象序列化的实现比较复杂,一般需要借助序列化工具。开源的序列化工具比较多,具体选择哪一个是受诸多因素约束的:

效率高;

前后向兼容性好;

支持异构系统;

稳定且被广泛使用;

接口友好;

...

下面我们比较几个常见的C++序列化工具。

msgpack是一个基于二进制的高效的对象序列化类库,可用于跨语言通信,号称比protobuf还要快4倍,但没有类似于optional的关键字,所以msgpack至少不满足前后项兼容的约束。

cereal是一个开源的(BSDLicense)、轻量级的、支持C++11特性的、仅仅包含头文件实现的、跨平台的C++序列化库。它可以将任意的数据类型序列化成不同的表现形式,比如二进制、XML格式或JSON。cereal的设计目标是快速、轻量级、易扩展——它没有外部的依赖关系,而且可以很容易的和其他代码封装在一块或者单独使用,但不能跨语言,所以cereal至少不满足异构系统系统的约束。

protobuf是一种轻便高效的结构化数据存储格式,可用于结构化数据串行化,很适合做数据存储或RPC数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

在PC上单线程测试protobuf的性能结果如下:

单位数量平均字节数35序列化(1w次)时间(us)反序列化(1w次)时间(us)通过表格来综合比较一下这三种序列化工具:

protobuf满足通信系统对序列化工具的选型约束,同时具有简单和高效的优点,所以protobuf比其他的序列化工具更具有吸引力。

protobufC++使用指导protobuf安装在github上下载protobufC++版本,并根据的说明进行安装,此处不再赘述。

定义.proto文件proto文件即消息协议原型定义文件,在该文件中我们可以通过使用描述性语言,来良好的定义我们程序中需要用到数据格式。

我们先通过一个簿的例子来了解下:

//otosyntax=proto3;packageApp;messagePerson{stringname=1;int32id=2;stringemail=3;enumPhoneType{MOBILE=0;HOME=1;WORK=2;}messagePhoneNumber{requiredstringnumber=1;optionalPhoneTypetype=2[default=HOME];}repeatedPhoneNumberphone=4;}messageAddressBook{repeatedPersonperson=1;}正你看到的一样,消息格式定义很简单,对于每个字段而言可能有一个修饰符(repeated)、字段类型(bool/string/bytes/int32等)和字段标签(Tag)组成。

对于repeated的字段而言,该字段可以重复多个,即用于标记数组类型。

对于protobufv2版本,除过repeated,还有required和optional,由于设计的不合理,在v3版本把这两个修饰符去掉了。

字段标签标示了字段在二进制流中存放的位置,这个是必须的,而且序列化与反序列化的时候相同的字段的Tag值必须对应,否则反序列化会出现意想不到的问题。

生成.文件进入protobuf的bin目录,输入命令:

./protoc-I=../../test/protobuf--cpp_out=../../test/protobuf../../test/protobuf/otoI的值为.proto文件的目录,cpp_out的值为.h和.cc文件生成的目录,运行该命令后,在$cpp_out路径下生成了.h和文件。

protobufC++API生成的文件中有以下方法:

//nameinlineboolhas_name()const;inlinevoidclear_name();inlineconst::std::stringname()const;inlinevoidset_name(const::std::stringvalue);inlinevoidset_name(constchar*value);inline::std::string*mutable_name();//idinlineboolhas_id()const;inlinevoidclear_id();inlineint32_tid()const;inlinevoidset_id(int32_tvalue);//emailinlineboolhas_email()const;inlinevoidclear_email();inlineconst::std::stringemail()const;inlinevoidset_email(const::std::stringvalue);inlinevoidset_email(constchar*value);inline::std::string*mutable_email();//phoneinlineintphone_size()const;inlinevoidclear_phone();inlineconst::google::protobuf::RepeatedPtrField::tutorial::Person_PhoneNumberphone()const;inline::google::protobuf::RepeatedPtrField::tutorial::Person_PhoneNumber*mutable_phone();inlineconst::tutorial::Person_PhoneNumberphone(intindex)const;inline::tutorial::Person_PhoneNumber*mutable_phone(intindex);inline::tutorial::Person_PhoneNumber*add_phone();解析与序列化接口:

/*序列化消息,将存储字节的以string方式输出,注意字节是二进制,而非文本;string!=text,tethatthebytesarebinary,nottext;weonlyusethestringclassasaconvenientcontainer.*/boolSerializeToString(string*output)const;//解析给定的stringboolParseFromString(conststringdata);AnyMessageTypeprotobuf在V3版本引入AnyMessageType。

顾名思义,AnyMessageType可以匹配任意的Message,包含Any类型的Message可以嵌套其他的Messages而不用包含它们的.proto文件。使用AnyMessageType时,需要import文件google/protobuf/oto。

syntax=proto3;packageApp;importgoogle/protobuf/oto;messageErrorStatus{ydetails=1;}messageNetworkErrorDetails{int32a=1;int32b=2;}messageLocalErrorDetails{int64x=1;stringy=2;}序列化时,通过pack操作将一个任意的Message存储到Any。

//p::NetworkErrorDetailsdetails;t_a(1);t_b(2);App::ErrorStatusstatus;d_details()-PackFrom(details);std::stringstr;rializeToString(str);反序列化时,通过unpack操作从Any中读取一个任意的Message。

//p::ErrorStatusstatus;std::stringstr;rseFromString(str);for(constgoogle::protobuf::Anydetail:tails()){if(App::NetworkErrorDetails()){App::NetworkErrorDetailswork_error;packTo(work_error);INFO_LOG(NetworkErrorDetails:%d,%d,work_error.a(),work_error.b());}}protobuf的最佳实践对象序列化设计序列化的单位为聚合或独立的实体,我们统一称为领域对象;

每个聚合可以引用其他聚合,序列化时将引用的对象指针存储为key,反序列化时根据key查询领域对象,将指针恢复为引用的领域对象的地址;

每个与序列化相关的类都要定义序列化和反序列化方法,可以通过通用的宏在头文件中声明,这样每个类只需









































治疗白癜风的最好医院
北京可以治疗白癜风的医院



转载请注明:http://www.nydjfy.com/xxzl/679.html

  • 上一篇文章:
  •   
  • 下一篇文章: