FlatBuffers快速入门

作者: GrayMonkey | 来源:发表于2017-05-10 12:28 被阅读435次

    FlatBuffers简介

    FlatBuffers是一个开源的、跨平台的、高效的、提供了C++/Java接口的序列化工具库。它是Google专门为游戏开发或其他性能敏感的应用程序需求而创建。尤其更适用于移动平台,这些平台上内存大小及带宽相比桌面系统都是受限的,而应用程序比如游戏又有更高的性能要求。它将序列化数据存储在缓存中,这些数据既可以存储在文件中,又可以通过网络原样传输,而不需要任何解析开销。

    特点

    1. 对序列化数据的访问不需要打包和拆包——它将序列化数据存储在缓存中,这些数据既可以存储在文件中,又可以通过网络原样传输,而没有任何解析开销;
    2. 内存效率和速度——访问数据时的唯一内存需求就是缓冲区,不需要额外的内存分配。 这里可查看详细的 基准测试
    3. 扩展性、灵活性——它支持的可选字段意味着不仅能获得很好的前向/后向兼容性(对于长生命周期的游戏来说尤其重要,因为不需要每个新版本都更新所有数据);
    4. 最小代码依赖——仅仅需要自动生成的少量代码和一个单一的头文件依赖,很容易集成到现有系统中。再次,看基准部分细节;
    5. 强类型设计——尽可能使错误出现在编译期,而不是等到运行期才手动检查和修正;
    6. 使用简单——生成的C++代码提供了简单的访问和构造接口;而且如果需要,通过一个可选功能可以用来在运行时高效解析Schema和类JSON格式的文本;
    7. 跨平台——支持C++11、Java,而不需要任何依赖库;在最新的gcc、clang、vs2010等编译器上工作良好;

    存储结构

    参考FaceBook的文档,假设我们有一个Person类定义如下:

    图-1

    假设有一个叫John的人,那么此Person对象在FlatBuffer中物理存储结构简化图如下,

    图-2

    1、每一个存储在FlatBuffer中的对象,被分割成2部分:中心点(Pivot Point)左边的元数据(vtable)和中心点右边的真实数据。
    2、每个字段都对应vtable中的一个槽(slot),它存储了那个字段的真实数据的偏移量。例如,John的vtable的第一个槽的值是1,表明John的名字被存储在John的中心点右边的一个字节的位置上。
    3、如果一个字段是一个对象,那么它在vtable中的偏移量会指向子对象的中心点。比如,John的vtable中第三个槽指向Mary的中心点。
    4、要表明某个字段现在没有数据,可以在vtable对应的槽中使用偏移量0来标注。

    Schema语言简介

    Schema语言(即IDL)的语法与C语言家族、AIDL很类似。

    Table

    Tables是在FlatBuffers中定义对象的主要方式,如下图所示,每一个字段都是可选的,可配置默认值(如果忽略的话,默认是0/NULL)。

    图-3

    Structs

    Structs与Table类似,不同点如下:
    1.无默认值,且字段不能增加或被废弃(deprecated)
    2.Struct只能包含标量或者其他struct
    如果非常确定数据结构不会发生任何变化,那么就可以使用struct。Struct使用的内存比Table少,并且读取时比Table更快(它们通常被以in-line的方式存储在它们的父对象中,并且不适用virtual table)
    Union、Enum功能与C语言类似

    版本兼容

    FlatBuffers具备良好的向前/向后兼容性,需要遵守以下规则:
    1.如果要在schema中添加新字段,只能在table的末尾进行添加
    2.如果不再使用某些字段了,不能从schema中删除它们;可以选择不再把它们写入到你的数据中,或者你通过deprecated来声明该字段不再使用,不过这样会使编译器不在产生该字段的代码,故存在一定的代码破坏性。

    假设服务器端更新了协议,新增了一个字段,而客户端并未更新,那么当客户端收到服务器的数据时,新增的字段对客户端而言等同于没有增加,客户端不会解析新增的字段,保证了向前兼容性。

    如果服务器协议回滚到一个低版本,那么当客户端收到服务器的数据进行解析时,客户端相较于服务端的新增字段,将会采用默认值,保证了向后兼容性。

    更多版本控制细节,请参考Schemas and version control

    支持的数据类型

    内建标量:

    图-4
    内建非标量类型:
    1.Vector ,其他数据类型的Vector (矢量),不支持内嵌
    2.string,只能存储UTF-8或者7-bit ASCII。如果需要存储其他编码的文本,或者通用二进制数据,请使用vector([byte]或者[ubyte])
    3.References,对其他table、struct、enum或者union的引用
    更多关于编写Schema的信息,请参考Writing Schema

    FlatBuffer使用步骤

    1.编写Schema文件
    2.使用FlatBuffers编译器生成java bean 文件,执行命令:
    flatc --java samples/monster.fbs
    编译器下载地址
    3.编译FlatBuffers的java库,下载flatbuffers源码,进入目录执行mvn package,选择Target目录的完整jar包(第一个)
    4.在Android工程中引入FlatBuffers的java库及编译器生成的java bean文件。
    5.使用FlatBufferBuilder构造一个ByteBuffer(序列化对象),保存或者发送该Buffer。
    6.读取该Buffer,通过getRootAsXXX可反序列化得到其代表的对象。
    关于FlatBuffers可读性差的问题,可通过执行命令:
    flatc --json person.fbs --raw-binary -- flat.bin
    直接将二进制文件转换为json格式的文件,解决可读性差的问题

    VS Java Serializable

    对比测试序列化与反序列化如下对象1000次所需要的执行时间,测试结果如下:

    FlatBuffers序列化耗时 FlatBuffers反序列化耗时 Java序列化耗时 Java反序列化耗时

    与传统的Java序列化相比,FlatBuffers在序列化及反序列化的速度上极具优势,在存储空间的占用上也具备一定的优势,节约近乎二倍的存储空间。测试Demo的对象比较简单,当序列化对象更为复杂的时候,FlatBuffers在解析速度与空间占用的优势将更为明显。
    Demo下载地址

    VS json

    与json相比,flatbuffers具备以下优点:
    1.解析速度快到飞起(json还要走一次java序列化,速度比Java序列化还慢)
    2.节约空间
    3.反序列化的时候无内存抖动,就一个ByteBuffer,内存占用平稳,json在反序列化的时候会产生大量临时对象,造成内存抖动。

    相关文章

      网友评论

        本文标题:FlatBuffers快速入门

        本文链接:https://www.haomeiwen.com/subject/vrsitxtx.html