使用 flatbuffers 已经有至关长的一段时间了.c++
在几个商用项目中, flatbuffers 也因快速的反序列化而带来性能上的很多提高.git
flatbuffers 尤为适合传输小块数据, 一次序列化, 多个地方进行反序列化.github
但 go 的 flatbuffers 有一些小遗憾:golang
在这样状况下, 我起了改进 go flatbuffers 的念头.api
flatbuffers 的编译器, 是 c++ 写的. 我已经不少年没有用过 c++ 开发了. 对我来讲, 这多是一次有趣的探险历程.bash
刚开始, 我写一个 flatbuffers verifier , 本地验证经过后, 我向 google flatbuffers 发了一个 PR. 结果被建议我重读一下 flatbuffers 的设计规范文档. 嗯哼, 这就开始有趣了.架构
在接下来的两周左右, 我边读 flatbuffers 的关键规范文档 ( 见附录参考列表) , 边写了一个全新的序列化生成器 ( flatbuffers builder ) .并发
我拆分了flatbuffers 的 memory block , 采用 goroutine 并发处理各个独立的 memory block 转化为二进制序列数据, 最后进行合并/排序/优化. 当这个手写序列化器看起来能够工做时, 我发现, 须要把这些手写代码嵌入 flatbuffers 编译器中, 支持自动代码生成, 我遇到了一个小难题. 我几乎忘记如何写 C++ 了.app
为此, 我重读了 Effective C++ 这样的几本册子, 随书写几行代码跑跑. 一周以后, 从新熟悉 C++ , 意外收获是对 go 的内存管理有了进一步的认识.post
如何让 go flatbuffers 序列化更快, 我还在尝试中.
而熟悉了 C++ 后, 我先让 go flatbuffers API 变得清晰简单, 易用一些.
union 是 flatbuffers 中颇有趣也颇有用的一个功能, 固然, struct 也颇有用. go flatbuffers 中, union 只支持 table , 而且不支持 union array ( 被称为 vector of unions ) , 先加上这个
IDL
union Character {
MuLan: Attacker, // table, 至关于 protobuf 中的 message
Rapunzel, // struct , 与 c++ 的 struct 至关
Belle: BookReader,
BookFan: BookReader,
Other: string, // string
Unused: string
}
table Movie {
main_character: Character; // 单一 union 字段
characters: [Character]; // vector of unions
}
复制代码
每个 fbs IDL 定义文件都支持各自的 module , 格式像这样: "go_module:github.com/tsingson/flatbuffers-sample/go-example/";
weapons.fbs
namespace weapons;
attribute "go_module:github.com/tsingson/flatbuffers-sample/samplesNew/";
table Gun {
damage:short;
bool:bool;
name:string;
names:[string];
}
复制代码
monster.fbs
include "../weapons.fbs";
namespace Mygame.Example;
attribute "go_module:github.com/tsingson/flatbuffers-sample/go-example/";
enum Color:byte { Red = 0, Green, Blue = 2 }
union Equipment { MuLan: Weapon, Weapon, Gun:weapons.Gun, SpaceShip, Other: string } // Optionally add more tables.
......
复制代码
生成的 go 代码
package Example
import (
"strconv"
flatbuffers "github.com/google/flatbuffers/go"
weapons "github.com/tsingson/flatbuffers-sample/samplesNew/weapons" /// 嗯哼!
)
type Equipment byte
..........
复制代码
weaponsOffset := flatbuffers.UOffsetT(0)
if t.Weapons != nil {
weaponsLength := len(t.Weapons)
weaponsOffsets := make([]flatbuffers.UOffsetT, weaponsLength)
for j := weaponsLength - 1; j >= 0; j-- {
weaponsOffsets[j] = t.Weapons[j].Pack(builder)
}
MonsterStartWeaponsVector(builder, weaponsLength) //////// start
for j := weaponsLength - 1; j >= 0; j-- {
builder.PrependUOffsetT(weaponsOffsets[j])
}
weaponsOffset = MonsterEndWeaponsVector(builder, weaponsLength) /////// end
}
复制代码
shortcut for []strings vector
// native object
Names []string
// builder
namesOffset := builder.StringsVector( t.Names...)
复制代码
getter for vector of unions
func (rcv *Movie) Characters(j int, obj *flatbuffers.Table) bool {
o := flatbuffers.UOffsetT(rcv._tab.Offset(10))
if o != 0 {
a := rcv._tab.Vector(o)
obj.Pos = a + flatbuffers.UOffsetT(j*4)
obj.Bytes = rcv._tab.Bytes
return true
}
return false
}
复制代码
so get struct or table
// GetStructVectorAsBookReader shortcut to access struct in vector of unions
func GetStructVectorAsBookReader(table *flatbuffers.Table) *BookReader {
n := flatbuffers.GetUOffsetT(table.Bytes[table.Pos:])
x := &BookReader{}
x.Init(table.Bytes, n+ table.Pos)
return x
}
// GetStructAsBookReader shortcut to access struct in single union field
func GetStructAsBookReader(table *flatbuffers.Table) *BookReader {
x := &BookReader{}
x.Init(table.Bytes, table.Pos)
return x
}
复制代码
for object-api , comments in generated code to make it clear
// UnPack use for single union field
func (rcv Character) UnPack(table flatbuffers.Table) *CharacterT {
switch rcv {
case CharacterMuLan:
x := GetTableAsAttacker(&table)
return &CharacterT{ Type: CharacterMuLan, Value: x.UnPack() }
.............
// UnPackVector use for vector of unions
func (rcv Character) UnPackVector(table flatbuffers.Table) *CharacterT {
switch rcv {
case CharacterMuLan:
x := GetTableVectorAsAttacker(&table)
return &CharacterT{ Type: CharacterMuLan, Value: x.UnPack() }
case CharacterRapunzel:
.........
复制代码
或许, 稍后更多, 让 Go flatbuffers ...... 更好用.
本文持续有更新...........
.
.
本文首发于 GolangChina , 在此 gocn.vip/topics/1022…
祝安康愉快!
_
_
网名 tsingson (三明智)
原 ustarcom IPTV/OTT 事业部播控产品线技术架构湿/解决方案工程湿角色(8年), 自由职业者,
喜欢音乐(口琴,是第三/四/五届广东国际口琴嘉年华的主策划人之一), 摄影与越野,
喜欢 golang 语言 (商用项目中主要用 postgres + golang )
tsingson ( 三明智 ) 于深圳南山. 小罗号口琴音乐中心 2020/04/09