FlatBuffers使用简介

@[tools|flatbuffers|opensource]

概述###

Google在今年6月份发布了跨平台序列化工具FlatBuffers,提供了C++/Java/Go/C#接口支持,这是一个注重性能和资源使用的序列化类库。相较于Protocol Buffers,其更适用于移动设备,FlatBuffers提供更高的性能以及更低的资源需求。

特点####

  • 不需要打包/解包。它的结构化数据都以二进制形式保存,不需要数据解析过程,数据也可以方便传递
  • 省内存、性能好
  • 强类型系统,在编译阶段就能预防一些bug的产生
  • 跨平台(C++11/Java/Go/C#)

FlatBuffers和Protocol Buffers以及Json的比较:####

  • FlatBuffers的功能和Protocol Buffers很像,他们的最大不同点是在使用具体的数据之前,FlatBuffers不需要解析/解包的过程。同时,在工程中使用时,FlatBuffers的引用比Protocol Buffers方便很多,只需要包含两三个头文件即可
  • JSON作为数据交换格式,被广泛用户各种动态语言之间(当然也包括静态语言)。它的优点是易于理解(可读性好),同时它的最大的缺点那就是解析时的性能问题了。而且因为它的动态类型特点,你的代码可能还需要多写好多类型、数据检查逻辑。

FlatBuffers的使用步骤####

  • 编写一个用来定义数据结构的schema(IDL,接口定义)文件,如下所示:
    //flatbuffers test struct

    namespace Jason.Flat.Test;

    enum Color : byte { Red = 1, Green, Blue }

    union Any { TextureData, Texture }

    table TestAppend {
        test_num:int;
        test_num2:int;
    }

    table TextureData {
        image_size:int (id:0);
        image_data:[ubyte] (id:1);
        image_test:short(id:3);
        test_num2:int(id:2);
    }

    table Texture {
        num_textures:short;
        textures:[TextureData];
        num_test:short = 30;
        num_test1:short (deprecated);
            num_test2:short;
        test_append:TestAppend;
    }

    root_type Texture;

将上述代码保存为TestFlat.fbs文件之后,即可用flatc来编译了

  • 使用FlatBuffer编译器flatc生成数据结构源代码(C++头文件或者Java类)
    登录GitHub,下载所需版本的源码及工程文件,在build目录下有VS2010的工程文件(当然,你也可以选择利用CMake自己本地创建工程),打开配置好flatc工程的传入参数,如下:
    -c -o ./ ./TestFlat.fbs
    运行flatc工程,即可在当前工程目录下生成TestFlat_generated.h头文件,这个头文件中包含了我们所需的所有结构体、枚举类型等以及相应的存取方法和验证方法
  1. 使用FlatBufferBuilder类创建flat的二进制buffer
    以下代码展示了如何利用FlatBufferBuilder创建相应buffer:
    //read serialized buffer
    flatbuffers::FlatBufferBuilder builder_data;

    int test_append = 300;
    auto name_test = builder_data.CreateString("TestAppend");
    auto testApp = CreateTestAppend(builder_data, test_append, test_append);

    int image_size = 12;
    unsigned char inv_data[] = { 11, 2, 4, 2, 10, 3, 5 ,7, 10, 39, 45, 23 };
    auto name = builder_data.CreateString("TextureData");
    auto image_data = builder_data.CreateVector(inv_data, image_size);

    int image_test = 900;
    auto texture_data = CreateTextureData(builder_data, image_size, image_data, image_test, image_test);

    //flatbuffers::FlatBufferBuilder builder_tex;
    int texture_num = 1;

    auto name_tex = builder_data.CreateString("Texture");

    vector<flatbuffers::Offset<TextureData>> tex_vec;
    tex_vec.push_back(texture_data);
    auto tex_data = builder_data.CreateVector(tex_vec);

    int num_text = 100, num_text2 = 200, num_text3 = 300;
    auto texture = CreateTexture(builder_data, texture_num, tex_data, num_text, num_text2, testApp);
    builder_data.Finish(texture);

要使上述正确运行,除了引用C++基本库之外,需在文件头部添加以下代码:

#include "flatbuffers/flatbuffers.h"
#include "idl.h"
#include "util.h"
#include "TestFlat_generated.h"
using namespace Jason::Flat::Test;

上述代码的编写中规中矩,其中CreateString和CreateVector都是FlatBufferBuilder类的成员函数,分别用于创建适用于FlatBuffer内存结构的字符串数据以及向量数据。其余的方法,如CreateTextureData、CreateTexture均是由flatc根据IDL文件(TestFlat.fbs)自动生成的头文件中用于创建相应结构体的函数。最后一句builder_data.Finish(texture)用于优化对齐写入builder_data的内存结构。

  1. 保存buffer到本地或者直接通过网络发送
    保存buffer到本地的代码,如下:
std::cout << builder_data.GetSize() << std::endl;
flatbuffers::SaveFile("texture.bin", reinterpret_cast<char *>(builder_data.GetBufferPointer()), builder_data.GetSize(), true);

将数据保存到名为texture.bin的二进制文件中,其中通过builder_data.GetBufferPointer()获取内存指针,builder_data.GetSize()获取内存大小,最后一个参数用于制定是否生成二进制文件。

  1. 接收并buffer并读取数据内容
    读取二进制文件的代码如下:
    string binaryfile;
    bool ok = flatbuffers::LoadFile("texture.bin", false, &binaryfile);

    flatbuffers::Verifier tex_verify(builderOut.GetBufferPointer(), builderOut.GetSize());
    bool verify_flag = VerifyTextureBuffer(tex_verify);

    flatbuffers::FlatBufferBuilder builderOut;
    TextureBuilder* texBuilder = new TextureBuilder(builderOut);
    builderOut.PushBytes(reinterpret_cast<unsigned char*>(const_cast<char *>(binaryfile.c_str())), binaryfile.size());
    std::cout << builderOut.GetSize() << std::endl;

    auto model = GetTexture(builderOut.GetBufferPointer());

    int outNum = model->num_textures();
    const flatbuffers::Vector<flatbuffers::Offset<TextureData>>* outTex = model->textures();
    TextureData* outTexData = (TextureData *)outTex->Get(0);
    int outSize = outTexData->image_size();
    const flatbuffers::Vector<unsigned char>* outData = outTexData->image_data();
    int x = outData->Get(5);
    int len = outData->Length();
    delete texBuilder;

上述代码中VerifyTextureBuffer用于验证读取的内存是否为FlatBuffers的内存块,是则返回true,不是则返回false。通过GetTexture获取指针之后,结构体中的变量均可以通过相应方法(各方法名请查看自动生成的头文件)获取。

总结####

利用FlatBuffers来进行数据保存及传输的优点显而易见,它利用自身特殊的编码格式,能一定程度上减少内存的占用,优化读取的性能。更重要的是,对于数据结构的向前向后兼容提供了很好的扩展性,方便又高效:

  • 要想让数据结构具有可扩展性,需将数据结构定义为table,它是数据扩展的基础,FlatBuffers中的struct类型不支持扩展
  • 如果想在后续的版本中删除数据结构中的某些字段,只要在将要删除的字段后面添加(deprecated)即可,当然需要保证删除的字段在之前版本的程序中不会引起程序崩溃(该删掉的字段在上一版本的程序中获取到的会是个空指针或空值,只需保证程序在获取到空值或空指针之后不会出现异常即可)
  • 如果想在后续版本中向数据结构中添加某些字段,需添加到table中最后一个字段的后面,若是想table中随意位置添加字段,需如上面TextureData 的定义,给每个字段指明添加id:n(n从0开始)

目前FlatBuffers还不是很完善,碰到问题可以到FlatBuffers Issues Tracker去提交或则寻找答案。

参考链接
FlatBuffers Documentation
github repository
Google FlatBuffers 跨平台序列化工具
FlatBuffers与protobuf性能比较

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容