开源跨平台数据格式化框架概览

简介:

说到数据格式化框架,就不得不提到 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。

  

 Bond 

 Protocol Buffers 

 Thrift 

 Avro 

框架起源

 Microsoft

 Google

 Facebook

 Apache

开源年份

2014

2008

2007

2009

开源协议

 MIT License 

BSD License

 Apache License 2.0 

 Apache License 2.0 

 代码位置 

GitHub 

GitHub 

Apache

 Apache

 官方文档 

 Documents 

 Documents

 Documents   Documents 

这些框架的一些共性:

这些框架的典型使用过程:

  • 编写类似于结构体的消息格式定义,使用类似于 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# 代码如下。

复制代码
 1 namespace serializers.protobuf.test
 2 {
 3   [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"SearchRequest")]
 4   public partial class SearchRequest : global::ProtoBuf.IExtensible
 5   {
 6     public SearchRequest() {}
 7     
 8     private string _query;
 9     [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"query", DataFormat = global::ProtoBuf.DataFormat.Default)]
10     public string query
11     {
12       get { return _query; }
13       set { _query = value; }
14     }
15     private int _page_number = default(int);
16     [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"page_number", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
17     [global::System.ComponentModel.DefaultValue(default(int))]
18     public int page_number
19     {
20       get { return _page_number; }
21       set { _page_number = value; }
22     }
23     private int _result_per_page = (int)10;
24     [global::ProtoBuf.ProtoMember(3, IsRequired = false, Name=@"result_per_page", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
25     [global::System.ComponentModel.DefaultValue((int)10)]
26     public int result_per_page
27     {
28       get { return _result_per_page; }
29       set { _result_per_page = value; }
30     }
31     private serializers.protobuf.test.SearchRequest.Corpus _corpus = serializers.protobuf.test.SearchRequest.Corpus.UNIVERSAL;
32     [global::ProtoBuf.ProtoMember(4, IsRequired = false, Name=@"corpus", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
33     [global::System.ComponentModel.DefaultValue(serializers.protobuf.test.SearchRequest.Corpus.UNIVERSAL)]
34     public serializers.protobuf.test.SearchRequest.Corpus corpus
35     {
36       get { return _corpus; }
37       set { _corpus = value; }
38     }
39     [global::ProtoBuf.ProtoContract(Name=@"Corpus")]
40     public enum Corpus
41     {            
42       [global::ProtoBuf.ProtoEnum(Name=@"UNIVERSAL", Value=0)]
43       UNIVERSAL = 0,
44             
45       [global::ProtoBuf.ProtoEnum(Name=@"WEB", Value=1)]
46       WEB = 1,
47     }
48   
49     private global::ProtoBuf.IExtension extensionObject;
50     global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
51       { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
52   }
53 }
复制代码

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 定义层的比较:

  

 Bond 

Protocol Buffers

 Thrift 

 Avro 

File Extension

 .bond

 .proto

 .thrift  .avpr

Namespace

 namespace 

 package

 namespace  @namespace

Import

 import "t.bond"

 import "t.proto"  include "t.thrift"  import protocol

 Compsite Type 

 struct t {} 

 message t {}

 struct t {} 

 protocol t {}

Tagging

 1: int32 t;

 int32 t = 1;

 1: i32 t

 ×

Field Rules

 required

 optional

 required

 optional

 repeated

 required

 optional

 -

Base Types

 bool

 uint8

 uint16

 uint32

 uint64

 float

 double

 string

 int8

 int16

 int32

 int64

 wstring

 double

 float

 int32

 int64

 uint32

 uint64

 sint32

 sint64

 fixed32

 fixed64

 sfixed32

 sfixed64

 bool

 string

 bytes

 bool

 byte

 i15

 i32

 i64

 double  

 string

 

 null

 boolean  

 int

 long

 float

 double

 bytes

 string

 

 Containers 

 list

 set

 map

 vector

 nullable

 blob

 ×

 list

 set

 map

 

 record

 array

 map

 union

 fixed

Polymorphism

 √  ×  ×  ×

Generics

 √  ×  ×  ×

Enumerations

 enum t {}

 enum t {}  enum t {} 

 enum t {}

Type Aliases

 using t = int64  ×  typedef i32 t  @aliases

Constants

 ×  ×  const i32 t = 1   ×

 Exceptions 

 √  ×  exception t {}  ×

Services

   service t {}  service t {}  protocol t {}

Attributes

 √  -  -  -

Comments

 C++ Style  C/C++ Style

 C/Shell Style

 Java Style

注:"√" 代表支持,"×" 代表不支持,"-" 代表不涉及。

编程语言支持

各开源数据格式化框架默认会支持若干编程语言,一些没有被默认支持的编程语言通常在社区中也会找到支持。下面是各框架默认支持的开发语言:

  

 Bond 

 Protocol Buffers 

 Thrift 

 Avro 

 官方支持语言 

 C#, C++,

 Python 

 C++, Java, 

 Python 

 C++, Java, Python, 

 PHP, Ruby, Erlang, 

 Perl, Haskell, C#, 

 Cocoa, JavaScript,

 Node.js, Smalltalk, 

 OCaml, Delphi 

 C, C++, C#, 

 Java, JavaScript, 

 Python, Perl,

 PHP, Ruby

开源语言实现

 

 C#:

 protobuf-net

 protobuf-csharp-port 

 Node.js:

 node-protobuf

   

性能比较

以下性能比较数据来自 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。









本文转自匠心十年博客园博客,原文链接:http://www.cnblogs.com/gaochundong/p/data_interchange_protocols.html,如需转载请自行联系原作者

目录
相关文章
|
8月前
|
存储 缓存 数据管理
HarmonyOS学习路之开发篇—数据管理(轻量级数据存储)
轻量级数据存储适用于对Key-Value结构的数据进行存取和持久化操作。应用获取某个轻量级存储对象后,该存储对象中的数据将会被缓存在内存中,以便应用获得更快的数据存取速度。应用也可以将缓存的数据再次写回文本文件中进行持久化存储,由于文件读写将产生不可避免的系统资源开销,建议应用减少对持久化文件的读写频率。
|
28天前
|
人工智能 供应链 监控
推荐一款TinyEngine低代码引擎!支持自定义DSL 生成定制的源码、跨技术栈!
推荐一款TinyEngine低代码引擎!支持自定义DSL 生成定制的源码、跨技术栈!
|
7月前
|
移动开发 小程序 开发工具
Donut 多端框架是支持使用小程序原生语法开发移动应用的框架
Donut 多端框架是支持使用小程序原生语法开发移动应用的框架,开发者可以一次编码,分别编译为小程序和 Android 以及 iOS 应用,实现多端开发
294 0
Donut 多端框架是支持使用小程序原生语法开发移动应用的框架
|
9月前
|
XML JSON 缓存
"Python与API集成:构建高效、灵活的数据交互平台"
首先,我们需要了解API的基本概念。API允许不同的软件系统之间相互通信和交换信息。它定义了不同应用程序之间如何请求和响应数据的规则和协议。使用API,我们可以方便地获取外部数据,将其集成到我们的应用程序中。
83 0
|
9月前
|
XML JSON 缓存
"Python与API:构建灵活强大的数据交互与应用开发平台"
在当今数字化时代,数据交互和应用开发已成为各行业发展的关键。而Python作为一种简洁、易学且功能强大的编程语言,以其丰富的库和模块凭借得天独厚的优势成为了开发人员的首选。同时,通过与API(应用程序接口)的结合,Python实现了无限可能,为构建灵活强大的数据交互和应用开发平台提供了有力支持。
87 0
|
10月前
|
缓存 安全 API
API技术解析:构建高效、可扩展的应用接口系统
在当前快速发展的互联网时代,构建高效、可扩展的应用接口系统对于企业的成功至关重要。而API技术作为实现应用程序之间数据交互的关键技术,成为了许多企业和开发者的首选。本文将深入探讨API技术在构建高效、可扩展的应用接口系统中的作用和应用,旨在帮助读者更好地理解和应用API技术。
120 0
|
11月前
|
Web App开发 JavaScript 算法
开源轻量级 IM 框架 MobileIMSDK 的Uniapp客户端库已发布
开源轻量级 IM 框架 MobileIMSDK 的Uniapp客户端库已发布
201 0
|
12月前
|
存储 测试技术 持续交付
06 接口测试平台:工具和框架不可以兼容?
06 接口测试平台:工具和框架不可以兼容?
|
JSON Cloud Native 中间件
导入导出框架AGEIPort(GEI)正式开源
AGEIPort 是数字供应链孵化并在阿里巴巴集团内广泛使用的一套性能卓越、稳定可靠、功能丰富、易于扩展、生态完整的数据导入导出方案,致力于帮助开发者在toB复杂业务场景下能够快速交付高性能、体验优、易维护的数据导入导出功能,如用户页面上的Excel/CSV数据文件上传和下载。 目前在阿里巴巴集团内部已有盒马、菜鸟、本地生活、阿里健康、钉钉、淘系等部门有较多使用,并成为多个技术组件的基础底座,经历多次618和双11大促考验,稳定导入导出数据300~400亿条/月。
37694 9
导入导出框架AGEIPort(GEI)正式开源
|
自然语言处理
云开发连接器支持多语言
云开发连接器支持多语言自制脑图
50 0
云开发连接器支持多语言