開源跨平臺資料格式化框架概覽
說到資料格式化框架,就不得不提到 Google 的 Protocol Buffers,Facebook 的 Thrift,還有 Apache Hadoop 推出的 Avro。Microsoft 最近開源的 Bond 也是一種用於資料格式化的可擴充套件框架,其適用的應用場景包括服務間通訊、大資料儲存和處理等。
為什麼會有這麼多關於資料格式處理的框架?它們都在解決什麼問題呢?我們先來觀察一下典型的服務間通訊的結構。
通常,在設計服務間通訊時,我們所要面對的基本問題有:
- 如何傳輸資料?
- 使用什麼協議通訊?
- 資料以何種格式表達?
- 在服務端如何處理資料請求?
- 資料在服務端如何儲存?
- 請求訊息如何路由或轉發?
隨著服務系統架構的不斷演進,我們會面對更多的問題:
- 適應架構演進的能力
- 適應叢集擴充套件的能力
- 靈活性
- 延時
- 簡單
那麼,以前我們都是在用什麼技術來解決這些問題的呢?
- C 語言的動態結構體二進位制傳輸
- DCOM, COM+
- CORBA
- SOAP
- XML, JSON
都是聽起來很熟悉的名字。實際上,C 結構體仍然被廣泛地應用於網路底層通訊,DCOM, CORBA, SOAP 正逐步退出歷史舞臺。目前,最流行的就是基於 XML 或 JSON 的序列化機制。
但使用 XML 和 JSON 時也會面對一些問題:
- 通訊協議需要額外描述
- 需要維護服務端和客戶端兩側契約程式碼
- 需要為設計的協議編寫包裝類
- 需要為不同程式語言編寫實現
- 承擔解析 XML 和 JSON 較高的開銷
- 儲存空間佔用相對較多
那麼,對於這些資料處理和序列化框架,從軟體設計人員的角度來看,我們最需要的到底是什麼呢?
- 多語言間的透明性
- 時間和空間效率
- 支援快速開發
- 能利用已有的類庫
所以,業界著名公司的開發人員分別推出了不同的框架,以期解決這些問題。包括 Google 的 Protocol Buffers,Facebook 的 Thrift,Apache Hadoop 的 Avro,和 Microsoft 的 Bond。
這些框架的一些共性:
- 使用 IDL 定義,IDL (Interface Description Language)
- 效能較高
- 支援版本演進
- 採用二進位制格式
這些框架的典型使用過程:
- 編寫類似於結構體的訊息格式定義,使用類似於 IDL 的語言定義。
- 使用程式碼生成工具,生成目標語言程式碼。
- 雖然生成了許多程式碼,但程式碼的可讀性比較高。
- 在程式中直接使用這些程式碼。
- 生成的程式碼不允許編輯。
也就是說,使用者首先需要定義資料結構,然後生成可以有效讀寫這些資料結構的程式碼,再將程式碼嵌入到服務端與客戶端的程式碼中使用。
例如,下面使用 Protocol Buffers 的定義搜尋請求訊息 search.proto。
package serializers.protobuf.test; message SearchRequest { required string query = 1; optional int32 page_number = 2; optional int32 result_per_page = 3 [default = 10]; enum Corpus { UNIVERSAL = 0; WEB = 1; } optional Corpus corpus = 4 [default = UNIVERSAL]; }
使用程式碼生成工具生成 C# 程式碼如下。
namespace serializers.protobuf.test { [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"SearchRequest")] public partial class SearchRequest : global::ProtoBuf.IExtensible { public SearchRequest() {} private string _query; [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"query", DataFormat = global::ProtoBuf.DataFormat.Default)] public string query { get { return _query; } set { _query = value; } } private int _page_number = default(int); [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"page_number", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] [global::System.ComponentModel.DefaultValue(default(int))] public int page_number { get { return _page_number; } set { _page_number = value; } } private int _result_per_page = (int)10; [global::ProtoBuf.ProtoMember(3, IsRequired = false, Name=@"result_per_page", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] [global::System.ComponentModel.DefaultValue((int)10)] public int result_per_page { get { return _result_per_page; } set { _result_per_page = value; } } private serializers.protobuf.test.SearchRequest.Corpus _corpus = serializers.protobuf.test.SearchRequest.Corpus.UNIVERSAL; [global::ProtoBuf.ProtoMember(4, IsRequired = false, Name=@"corpus", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] [global::System.ComponentModel.DefaultValue(serializers.protobuf.test.SearchRequest.Corpus.UNIVERSAL)] public serializers.protobuf.test.SearchRequest.Corpus corpus { get { return _corpus; } set { _corpus = value; } } [global::ProtoBuf.ProtoContract(Name=@"Corpus")] public enum Corpus { [global::ProtoBuf.ProtoEnum(Name=@"UNIVERSAL", Value=0)] UNIVERSAL = 0, [global::ProtoBuf.ProtoEnum(Name=@"WEB", Value=1)] WEB = 1, } private global::ProtoBuf.IExtension extensionObject; global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); } } }
IDL 語法
使用 IDL 定義的語法通常包括:
- 每個欄位(Field)必須包含一個唯一的正整數識別符號,例如 “= 1″, “= 2″ 或 “1 : “, “2 : ” 等。
- 欄位可以被標記為 required 或 optional。
- 多個 structs 可以被定義在相同的檔案中。
- structs 可以包含其他 structs。
- 欄位可以被指定預設值。
這裡,為欄位指定的識別符號 “= 1″, “= 2″ 或 “1 : “, “2 : ” 等稱為 “Tag”,這個操作稱為 “Tagging”。這些 Tag 用於從二進位制的訊息中識別欄位,所以一旦定義並使用,則後續不能修改。
Tag 的值在 1-15 區間時使用 1 byte 儲存,在 16-2047 區間時使用 2 bytes 儲存。所以,為節省空間,要將 1-15 留給最常使用的訊息元素,並且要為未來可能出現的頻繁使用元素留出空間。
下面是各框架在 IDL 定義層的比較:
注:”√” 代表支援,”×” 代表不支援,”-” 代表不涉及。
程式語言支援
各開源資料格式化框架預設會支援若干程式語言,一些沒有被預設支援的程式語言通常在社群中也會找到支援。下面是各框架預設支援的開發語言:
效能比較
以下效能比較資料來自 GitHub eishay/jvm-serializers 。
Serializes only specific classes using code generation or other special knowledge about the class.
create ser deser total size +dfl kryo-opt 64 658 864 1522 209 129 wobly 43 886 536 1422 251 151 wobly-compact 43 903 569 1471 225 139 protobuf 130 1225 701 1926 239 149 protostuff 82 488 678 1166 239 150 protobuf/protostuff 83 598 692 1290 239 149 thrift 126 1796 795 2591 349 197 thrift-compact 126 1555 963 2518 240 148 avro 89 1616 1415 3031 221 133 json/json-lib-databind 63 26330 103150 129479 485 263 json/jsonij-jpath 63 38015 12325 50339 478 259
Total Time : Including creating an object, serializing and deserializing.
Serialization Time : Serializing with a new object each time (object creation time included).
Deserialization Time : Often the most expensive operation. To make a fair comparison, all fields of the deserialized instances are accessed – this forces lazy deserializers to really do their work.
Serialization Size : May vary a lot depending on number of repetitions in lists, usage of number compacting in protobuf, strings vs numerics, assumptions that can be made about the object graph, and more.
Object Creation Time : Object creation is not so meaningful since it takes in average 100 nano to create an object.
版本演進
各資料格式化框架通過 Tag 來支援版本控制,以支援前向和後向相容。客戶端與服務端的版本不匹配可歸納為 4 種情況:
- 新增欄位,舊 Client,新 Server;
- 新增欄位,新 Client,舊 Server;
- 刪除欄位,舊 Client,新 Server;
- 刪除欄位,新 Client,舊 Server;
正常情況:Client 和 Server 一致;
Client 端和 Server 端訊息定義欄位一致。
情況1:新增欄位,舊 Client,新 Server;
Client 端仍然使用舊版本。
Server 端訊息定義新增欄位 branch_id。
Server 端需要適配舊版本,也就是處理沒有 branch_id 的訊息。
情況2:新增欄位,新 Client,舊 Server;
Client 端訊息定義新增欄位 branch_id。
Server 端仍然使用舊版本。
Server 端解析訊息時實際上直接忽略了 branch_id 欄位,所以不會產生問題。
情況3:刪除欄位,舊 Client,新 Server;
參考情況2。
情況4:刪除欄位,新 Client,舊 Server;
參考情況1。
相關文章
- 微軟開源 .NET 框架 實現跨平臺微軟框架
- cross-plateform 跨平臺應用程式-01-概覽ROSORM
- 跨平臺開發框架的大旗框架
- 跨平臺開發框架 Lynx 初探框架
- Office 365 API平臺概覽API
- 雲端計算平臺概覽
- 原生體驗擋不住!JavaScript開源跨平臺框架NativeScriptJavaScript框架
- 各種SmartPhone上的跨平臺開源框架的總結框架
- .NET平臺下開源框架框架
- “低程式碼”平臺特性概覽
- 【開源】C#跨平臺物聯網通訊框架ServerSuperIO(SSIO)C#框架Server
- 一應俱全!開源跨平臺3D應用開發框架Minko3D框架
- 跨平臺開發優選則!高效率、美觀限制少,Flutter 技術概覽!Flutter
- 跨平臺整合所有資料資源,ODI簡單介紹
- MediaPipe - 跨平臺機器學習應用開發框架API機器學習框架
- 推理框架概覽框架
- Flutter框架概覽Flutter框架
- 《離線和實時大資料開發實戰》(二)大資料平臺架構 & 技術概覽大資料架構
- 一款開源的跨平臺實時web應用框架——DotNetifyWeb框架
- 開源GTKSystem.Windows.Forms框架讓C# winform支援跨平臺執行WindowsORM框架C#
- 跨平臺開源通訊元件elastic communication元件AST
- 4個.Net跨平臺圖形開源庫
- 概覽資料庫索引資料庫索引
- ComPDFKit: 跨平臺框架PDF SDK框架
- 微軟借力.NET開源跨平臺支援,佈局物聯網平臺開發微軟
- 開源、高效、跨平臺:深剖Google FlatBuffers工作原理Go
- 開源 Amundsen:資料發現和後設資料平臺
- 開源的 P2P 跨平臺傳檔案應用「GitHub 熱點速覽」Github
- zt 跨平臺 跨版本 大規模資料遷移
- GStreamer跨平臺多媒體框架框架
- 概覽,如何管控資料
- Tcloud 雲測平臺-多服務框架開源Cloud框架
- 使用RMAN完成跨平臺資料遷移
- 利用RMAN跨平臺遷移資料庫資料庫
- rman進行跨平臺資料遷移
- 跨平臺遷移oracle資料庫指南Oracle資料庫
- long資料型別跨平臺問題資料型別
- 跨平臺資料庫 Realm 整合實踐資料庫