Flatbuffer的簡單使用

littlexiaoshuishui發表於2021-10-09

安裝使用

參考連結:blog.51cto.com/onebig/2122019

一、FlatBuffers簡介

FlatBuffers為Google釋出的一個跨平臺,提供多種語言介面,注重效能和資源使用的序列化類庫。目前該類庫提供C++, C#, C, Go, Java, JavaScript, PHP, and Python語言介面。該序列化類庫多用於移動端手遊資料傳輸以及特定的對效能有較高要求的應用。 接下來我們將學習FlatBuffers環境搭建並且使用Java語言完成一次簡單的序列化例子。

  • 編譯flatc工具

  • 編寫一個FlatBuffers的scheme檔案,.fbs檔案

  • 使用flatc工具編譯scheme檔案,生成對應語言的資料物件標頭檔案/類

  • 使用FlatBufferBuilder序列化物件

  • 反序列化資料物件

編譯flatc工具

下載flatc原始碼

git clone git@github.com:google/flatbuffers.git
cd flatbuffers/
sudo cmake -G "Unix Makefiles" //生成makefile檔案
sudo make 
sudo make install
flatc --version  //顯示版本

編寫FlatBuffers的scheme檔案

基本型別:

  • 8 bit: byte ubyte bool

  • 16 bit: short ushort

  • 32 bit: int uint float

  • 64 bit: long ulong double

複雜型別:

  • 陣列 (用中括號表示 [type]). 不支援巢狀陣列,可以用table實現

  • 字串 string, 支援 UTF-8 或者 7-bit ASCII. 對於其他編碼可以用陣列 [byte]或者[ubyte]表示。

  • Struct 只支援基本型別或者巢狀Struct

  • Table 類似Struct,但是可以支援任何型別

先編寫test.fbs

namespace Block;
​
table Block {
 id: long;
 hash: string;
 flag: bool;
 txs: [Tx];
}
table Tx {
 hash: string;
 value: double;
}
​
root_type Block;

參考連結:github.com/halfrost/Halfrost-Field...

Go使用FlatBuffers

編譯scheme檔案為go檔案,會生成Block.go, Tx.go檔案

flatc -g myschema.fbs

安裝go擴充套件包

go get -v github.com/google/flatbuffers/go
序列化資料

把物件轉為二進位制資料

package main
import (
  "fmt"
  fbs "letcode/flatbuff/fbs/block"
  flatbuffers "github.com/google/flatbuffers/go"
)
type Block struct {
  Id   int64
  Hash string
  Flag bool
  Txs  []Tx
}
type Tx struct {
  Hash  string
  Value float64
}
func main() {
  txone := Tx{Hash: "adfadf", Value: 123}
  txtwo := Tx{Hash: "adfadf", Value: 456}
  block := Block{Id: 1,Hash: "aadd",Flag: true,}
  //初始化buffer,大小為0,會自動擴容
  builder := flatbuffers.NewBuilder(0)
  //第一個交易
  txoneh := builder.CreateString(txone.Hash)//先處理非標量string,得到偏移量
  fbs.TxStart(builder)
  fbs.TxAddHash(builder, txoneh)
  fbs.TxAddValue(builder, txone.Value)
  ntxone := fbs.TxEnd(builder)
  //第二個交易
  txtwoh := builder.CreateString(txtwo.Hash)
  fbs.TxStart(builder)
  fbs.TxAddHash(builder, txtwoh)
  fbs.TxAddValue(builder, txtwo.Value)
  ntxtwo := fbs.TxEnd(builder)
  //block
  //先處理陣列,string等非標量
  fbs.BlockStartTxsVector(builder, 2)
  builder.PrependUOffsetT(ntxtwo)
  builder.PrependUOffsetT(ntxone)
  txs := builder.EndVector(2)
  bh := builder.CreateString(block.Hash)
  //再處理標量
  fbs.BlockStart(builder)
  fbs.BlockAddId(builder, block.Id)
  fbs.BlockAddHash(builder, bh)
  fbs.BlockAddFlag(builder, block.Flag)
  fbs.BlockAddTxs(builder, txs)
  nb := fbs.BlockEnd(builder)
  builder.Finish(nb)
  buf := builder.FinishedBytes() //返回[]byte
  fmt.Println(buf)
}

要序列化block,首先處理非標量的序列化,得到偏移量。

反序列化

讀取二進位制資料轉為物件

func DecodeToBlock(filename string) Block {
    var (
        block Block
    )
    buf, err := ioutil.ReadFile(filename)
    if err != nil {
        panic(err)
    }
    //傳入二進位制資料
    b := fbs.GetRootAsBlock(buf, 0)
    block.Flag = b.Flag()
    block.Hash = string(b.Hash())
    block.Id = b.Id()
    len := b.TxsLength()
    for i := 0; i < len; i++ {
        tx := new(fbs.Tx)
        ntx := new(Tx)
        if b.Txs(tx, i) {
            ntx.Hash = string(tx.Hash())
            ntx.Value = tx.Value()
        }
        block.Txs = append(block.Txs, *ntx)
    }
    return block
}
  1. 編碼效能:flatbuf 的編碼效能要比 protobuf 低。在JSON、protobuf 和 flatbuf 之中,flatbuf 編碼效能最差,JSON 介於二者之間。

  2. 編碼後的資料長度:由於通常情況下,傳輸的資料都會做壓縮。在不壓縮的情況下,flatbuffer 的資料長度是最長的,理由也很簡單,因為二進位制流內部填充了很多位元組對齊的 0,並且原始資料也沒有采取特殊的壓縮處理,整個資料膨脹的更大了。不管壓不壓縮,flatbuffer 的資料長度都是最長的。JSON 經過壓縮以後,資料長度會近似於 protocol buffer。protocol buffer 由於自身編碼就有壓縮,再經過 GZIP 這些壓縮演算法壓縮以後,長度始終維持最小。

  3. 解碼效能:flatbuffer 是一種無需解碼的二進位制格式,因而解碼效能要高許多,大概要比 protobuf 快幾百倍的樣子,因而比 JSON 快的就更多了。

結論是,如果需要重度依賴反序列化的場景,可以考慮用 flatbuffer。protobuf 則是表現出各方面都很均衡的能力。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
用過哪些工具?為啥用這個工具(速度快,支援高併發...)?底層如何實現的?