go语言中Flatbuffer的安装及简单使用流程

2023-06-01 00:00:00 语言 简单 流程

FlatBuffers 为 Google 发布的一个跨平台,提供多种语言接口,注重性能和资源使用的序列化类库。

目前该类库提供 C++, C#, C, Go, Java, JavaScript, PHP, and Python 语言接口。

该序列化类库多用于移动端手游数据传输以及特定的对性能有较高要求的应用。 

接下来我们将学习 FlatBuffers 环境搭建并且使用 Java 语言完成一次简单的序列化例子。


编译 flatc 工具

编写一个 FlatBuffers 的 scheme 文件,.fbs 文件

使用 flatc 工具编译 scheme 文件,生成对应语言的数据对象头文件/类

使用 FlatBufferBuilder 序列化对象

反序列化数据对象


编译 flatc 工具

下载 flatc 源码

git clone [email protected]: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;

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
}


扩展知识:

编码性能:

flatbuf 的编码性能要比 protobuf 低。在 JSON、protobuf 和 flatbuf 之中,flatbuf 编码性能最差,JSON 介于二者之间。


编码后的数据长度:

由于通常情况下,传输的数据都会做压缩。在不压缩的情况下,flatbuffer 的数据长度是最长的,理由也很简单,因为二进制流内部填充了很多字节对齐的 0,并且原始数据也没有采取特殊的压缩处理,整个数据膨胀的更大了。不管压不压缩,flatbuffer 的数据长度都是最长的。JSON 经过压缩以后,数据长度会近似于 protocol buffer。protocol buffer 由于自身编码就有压缩,再经过 GZIP 这些压缩算法压缩以后,长度始终维持最小。


解码性能:

flatbuffer 是一种无需解码的二进制格式,因而解码性能要高许多,大概要比 protobuf 快几百倍的样子,因而比 JSON 快的就更多了。


总结

如果需要重度依赖反序列化的场景,可以考虑用 flatbuffer。

protobuf 则是表现出各方面都很均衡的能力。

相关文章