86286933 2014-08-20
前段时间刚试用了一个序列化工具cereal,请看cereal:C++实现的开源序列化库,打算再总结下我对google proto buf序列化库的使用呢,
结果还没动手,大Google又出了一个新的、开源、跨平台的序列化工具:FlatBuffers。那就索性先了解了解这个工具把。
一. 什么是Google FlatBuffers
FlatBuffers是一个开源的、跨平台的、高效的、提供了C++/Java接口的序列化工具库。它是Google专门为游戏开发或其他性能敏感的应用程序需求而创建。尤其更适用于移动平台,这些平台上内存大小及带宽相比桌面系统都是受限的,而应用程序比如游戏又有更高的性能要求。它将序列化数据存储在缓存中,这些数据既可以存储在文件中,又可以通过网络原样传输,而不需要任何解析开销。
代码托管主页:https://github.com/google/flatbuffers;
项目介绍主页:http://google.github.io/flatbuffers/index.html;
二. 为什么要使用Google FlatBuffers
三. 为什么不使用Protocol Buffers的,或者JSON
Protocol Buffers的确和FlatBuffers比较类似,但其主要区别在于FlatBuffers在访问数据前不需要解析/拆包这一步。 而且Protocol Buffers既没有可选的文本导入/导出功能,也没有Schemas语法特性(比如union)。
JSON是非常可读的,而且当和动态类型语言(如JavaScript)一起使用时非常方便。然而在静态类型语言中序列化数据时,JSON不但具有运行效率低的明显缺点,而且会让你写更多的代码来访问数据(这个与直觉相反)。
想了解更多关于FlatBuffers的“为什么”请访问flatbuffers白皮书。
四. 内建的数据类型
byte ubyte boolshort ushortint uint floatlong ulong double[type]). Nesting vectors is not supported, instead you can wrap the inner vector in a table.[byte] or [ubyte]) instead.详细介绍请参考:schema语法格式。
五. 如何使用
六. 一个简单的Schemas(IDL)文件
namespace zl.persons;
enum GENDER_TYPE : byte
{
MALE = 0,
FEMALE = 1,
OTHER = 2
}
table personal_info
{
id : uint;
name : string;
age : byte;
gender : GENDER_TYPE;
phone_num : ulong;
}
table personal_info_list
{
info : [personal_info];
}
root_type personal_info_list;注意:这里有table、struct的区别:
table是Flatbuffers中用来定义对象的主要方式,和struct最大的区别在于:它的每个字段都是可选的(类似protobuf中的optional字段),而struct的所有成员都是required。
table除了成员名称和类型之外,还可以给成员一个默认值,如果不显式指定,则默认为0(或空)。struct不能定义scalar成员,比如说string类型的成员。在生成C++代码时,struct的成员顺序会保持和IDL的定义顺序一致,如果有必要对齐,生成器会自动生成用于对齐的额外成员。如以下Schemas代码:
struct STest
{
a : int;
b : int;
c : byte;
}在生成为C++代码之后,会补充两个用于padding的成员__padding0与__padding1:
MANUALLY_ALIGNED_STRUCT(4) STest {
private:
int32_t a_;
int32_t b_;
int8_t c_;
int8_t __padding0;
int16_t __padding1;
public:
STest(int32_t a, int32_t b, int8_t c)
: a_(flatbuffers::EndianScalar(a)), b_(flatbuffers::EndianScalar(b)), c_(flatbuffers::EndianScalar(c)), __padding0(0) {}
int32_t a() const { return flatbuffers::EndianScalar(a_); }
int32_t b() const { return flatbuffers::EndianScalar(b_); }
int8_t c() const { return flatbuffers::EndianScalar(c_); }
};
STRUCT_END(STest, 12);table的成员顺序是动态调整的,这和struct有区别。在生成C++代码时,生成器会自动调整为最佳顺序以保证它占用最小的内存空间。
七. 一个完整Demo
这里只给一个函数演示如何对对象进行序列化,完整工程请直接点击下载,或者前往github查看google_flatbuffers_test。
std::string CreateOnePerson()
{
flatbuffers::FlatBufferBuilder builder;
fb_offset<fb_string> name = builder.CreateString("hello word");
zl::persons::personal_infoBuilder pib(builder);
pib.add_id(1);
pib.add_age(25);
pib.add_gender(zl::persons::GENDER_TYPE_MALE);
pib.add_name(name);
pib.add_phone_num(1234567890);
flatbuffers::Offset<zl::persons::personal_info> personinfo = pib.Finish();
fb_offset<zl::persons::personal_info> info[1];
info[0] = personinfo;
fb_offset<fb_vector<fb_offset<zl::persons::personal_info>>> info_array = fb_create_vector(builder, info, sizeof(info) / sizeof(info[0]));
fb_offset<zl::persons::personal_info_list> info_list = create_personal_info_list(builder, info_array);
fb_finish(builder, info_list);
// return the buffer for the caller to use.
return std::string(reinterpret_cast<const char *>(builder.GetBufferPointer()), builder.GetSize());
}八. 其他
关于性能,除了Google公布的基准测试外,有人自己测试验证过,上面的IDL文件即来源于该作者的这篇文章。
九. 参考
http://google.github.io/flatbuffers/index.html
http://powman.org/archives/md__schemas.html
http://blog.csdn.net/menggucaoyuan/article/details/34409433
http://liubin.org/2014/06/19/google-flatbuffers-cross-platform-serialization-library/
spring-data-redis RedisTemplate 操作redis时发现存储在redis中的key不是设置的string值,前面还多出了许多类似\xac\xed\x00\x05t\x00;