使用 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