Protocol Buffers是Google跨語言、跨平臺的通用序列化庫。FlatBuffers一樣出自Google,並且也跨語言跨平臺,但更強調效率,專門爲遊戲開發打造。在遊戲界混了幾年,各類各樣的序列化協議都見過,MUD的字符串、Json、二進制、Protocol Buffers,各有各的優缺點。前端
Protocol Buffers採用的是單個字段壓縮到數組的方式。例如:node
message CPing { int32 x = 1; int32 y = 2; int32 z = 3; int32 way = 4; }
則字段x的索引爲1,y的索引爲2,依此類推,最終通過Protocol Buffers把索引、數據都壓縮後,在內存中大概是這樣排列的:android
FlatBuffers則採用內存映射的方式,例如:git
table CPing { x:int; y:string; }
參考C結構體在內存中的結構模型,像int這種內存不變的,稱爲POD類型,沒法預先知道長度的(好比字符串),稱爲指針類型。FlatBuffers直接把內存中結構體類型直接搬到了序列化內存中。header老是在最前端,記錄了各個成員的位置。各個成員的位置若是是POD類型,則記錄數據,若是是指針類型,則記錄數據位置。而後經過嚴格的內存對齊參數,用編譯器實現跨語言、跨平臺。大概是這樣:github
Protocol Buffers和FlatBuffers具體的序列化、反序列化還有不少細節,好比壓縮算法、內存如何對齊,這裏難以詳細說明,有興趣能夠本身去查資料。算法
我本身業餘實現了一個服務器框架,以C++爲底層,Lua做爲上層邏輯腳本。爲了提升開發效率,全部消息到達腳本時都會自動序列化爲Lua的table,不須要開發人員去解析數據包。例如:ubuntu
message CPing { int32 x = 1; int32 y = 2; int32 z = 3; int32 way = 4; }
到達腳本時,就會是一個table,如:數組
{ x = 999, y = 123, z = 777, way = 0, }
所使用的庫爲:服務器
Protocol Buffers:https://github.com/cloudwu/pbc框架
Flatbuffers:https://github.com/changnet/lua_flatbuffers
如今爲了測試打包、解包效率,設計了這麼一個流程:
玩家actor的數據包先通過網關gateway,再由網關轉發給遊戲世界world。而後world會返回數據給gateway,再轉發給actor。開啓4個進程,每一個進程登陸2500個玩家,每一個玩家1000個數據包,每秒發送8個,因此最快是1000 / 8 = 125秒。系統爲ubuntu 14.04,Docker版本 17.03.1-ce, build c6d412e ,程序編譯參數爲-g0 -O2,全部進程運行在同一Docker中,機器配置爲hp probook 4446(cpu爲A8-4500m):
CPU MHz: 1400.000 BogoMIPS: 3792.91 Virtualization: AMD-V L1d cache: 16K L1i cache: 64K L2 cache: 2048K NUMA node0 CPU(s): 0-3
數據包爲:
// 玩家發包 message CPing { int32 x = 1; int32 y = 2; int32 z = 3; int32 way = 4; } // 服務器回包 message SPing { int32 time = 1; }
Protocol Buffers的成績爲:
FlatBuffers的成績爲:
能夠看到,兩個庫的效率相差無幾(其實由於發的數據包太簡單,徹底看不出來),可是Protocol Buffer的world進程使用的cpu較高,而gateway較低,說明打包消耗了更多的cpu和時間,可是轉發時流量小,IO更低。而FlatBuffers則反過來了。
上面測試的例子比較簡單,都沒有數組和字符串,在發送的數據加上數組和字符串:
message CPing { int32 x = 1; int32 y = 2; int32 z = 3; int32 way = 4; repeated int32 target = 5; string say = 6; }
發送的時候,數組固定爲:{ 1,2,3,4,5,6,7,8,9 }而字符串固定爲:"android ping test android ping test android ping test android ping test android ping test"。同時玩家的數量減爲5000,進程改成個,依然是每一個進程2500個玩家。
Protocol Buffers成績:
FlatBuffers成績爲:
能夠看到,加上數組和字符串後,FlatBuffers消耗的cpu資源遠小於ProtocolBuffers,可是效率上的差距由於測試的方法不當則看不出來。
看到這裏,你們可能很不理解我測試的方式,cpu基本沒有跑滿過。首先,我沒辦法讓cpu恰好跑滿,由於玩家那個進程是採用定時發包的方式來模擬玩家操做而不是echo方式(收到回包後再發包),因此多個玩家進程懟一個服務器進程,只要gateway和world進程有一個吃不消,就會形成數據包堆積。其次,我作這個測試是爲了證實這兩個庫集成到個人框架中後可否達到我指望的效率。再着,這個測試中Lua的gc消耗多是影響最大的一個因素。因此,這裏只是一個參考,若是你要單純測試這兩個庫的效率,能夠直接測試那兩個庫(網上已經有很多結果了)。
FlatBuffers的效率要高一些,而Protocol Buffers的流量要小一些,並且Protocol Buffers的使用更加普遍、成熟。項目中使用哪一個,就要看我的取捨。其實,我最先寫了一個二進制序列化的庫,使用Json做爲schema文件,也能達到自動打包、解包的效果。可是不能實現版本向後兼容,也不能實現字段冗餘,不過效率比這兩個都要高。後來我嫌棄本身代碼寫得爛,就從框架分離出去了,等有時間再整理成一個獨立的庫,放在https://github.com/changnet/lua_stream。
測試框架代碼在https://github.com/changnet/MServer,是一個半成品,一直在忙其餘的,沒空完善。