序列化和反序列化

一,幾種常見的序列化和反序列化協議

互聯網早期的序列化協議主要有COM和CORBA。

COM主要用於Windows平臺,並沒有真正實現跨平臺,另外COM的序列化的原理利用了編譯器中虛表,使得其學習成本巨大(想一下這個場景, 工程師需要是簡單的序列化協議,但卻要先掌握語言編譯器)。由於序列化的數據與編譯器緊耦合,擴展屬性非常麻煩。

CORBA是早期比較好的實現了跨平臺,跨語言的序列化協議。COBRA的主要問題是參與方過多帶來的版本過多,版本之間兼容性較差,以及使用複雜晦澀。這些政治經濟,技術實現以及早期設計不成熟的問題,最終導致COBRA的漸漸消亡。J2SE 1.3之後的版本提供了基於CORBA協議的RMI-IIOP技術,這使得Java開發者可以採用純粹的Java語言進行CORBA的開發。

這裏主要介紹和對比幾種當下比較流行的序列化協議,包括XML、JSON、Protobuf、Thrift和Avro。

XML&SOAP

XML是一種常用的序列化和反序列化協議,具有跨機器,跨語言等優點。 XML歷史悠久,其1.0版本早在1998年就形成標準,並被廣泛使用至今。XML的最初產生目標是對互聯網文檔(Document)進行標記,所以它的設計理念中就包含了對於人和機器都具備可讀性。 但是,當這種標記文檔的設計被用來序列化對象的時候,就顯得冗長而複雜(Verbose and Complex)。 XML本質上是一種描述語言,並且具有自我描述(Self-describing)的屬性,所以XML自身就被用於XML序列化的IDL。 標準的XML描述格式有兩種:DTD(Document Type Definition)和XSD(XML Schema Definition)。作爲一種人眼可讀(Human-readable)的描述語言,XML被廣泛使用在配置文件中,例如O/R mapping、 Spring Bean Configuration File 等。

SOAP(Simple Object Access protocol) 是一種被廣泛應用的,基於XML爲序列化和反序列化協議的結構化消息傳遞協議。SOAP在互聯網影響如此大,以至於我們給基於SOAP的解決方案一個特定的名稱--Web service。SOAP雖然可以支持多種傳輸層協議,不過SOAP最常見的使用方式還是XML+HTTP。SOAP協議的主要接口描述語言(IDL)是WSDL(Web Service Description Language)。SOAP具有安全、可擴展、跨語言、跨平臺並支持多種傳輸層協議。如果不考慮跨平臺和跨語言的需求,XML的在某些語言裏面具有非常簡單易用的序列化使用方法,無需IDL文件和第三方編譯器, 例如Java+XStream。

自我描述與遞歸

SOAP是一種採用XML進行序列化和反序列化的協議,它的IDL是WSDL. 而WSDL的描述文件是XSD,而XSD自身是一種XML文件。 這裏產生了一種有趣的在數學上稱之爲“遞歸”的問題,這種現象往往發生在一些具有自我屬性(Self-description)的事物上。

IDL文件舉例

採用WSDL描述上述用戶基本信息的例子如下:

<xsd:complexType name='Address'>
	 <xsd:attribute name='city' type='xsd:string' />
	 <xsd:attribute name='postcode' type='xsd:string' />
	 <xsd:attribute name='street' type='xsd:string' />
</xsd:complexType>
<xsd:complexType name='UserInfo'>
	 <xsd:sequence>
	 <xsd:element name='address' type='tns:Address'/>
	 <xsd:element name='address1' type='tns:Address'/> 
	 </xsd:sequence>
	 <xsd:attribute name='userid' type='xsd:int' />
	 <xsd:attribute name='name' type='xsd:string' /> 
</xsd:complexTyp>

典型應用場景和非應用場景

SOAP協議具有廣泛的羣衆基礎,基於HTTP的傳輸協議使得其在穿越防火牆時具有良好安全特性,XML所具有的人眼可讀(Human-readable)特性使得其具有出衆的可調試性,互聯網帶寬的日益劇增也大大彌補了其空間開銷大(Verbose)的缺點。對於在公司之間傳輸數據量相對小或者實時性要求相對低(例如秒級別)的服務是一個好的選擇。由於XML的額外空間開銷大,序列化之後的數據量劇增,對於數據量巨大序列持久化應用常景,這意味着巨大的內存和磁盤開銷,不太適合XML。另外,XML的序列化和反序列化的空間和時間開銷都比較大,對於對性能要求在ms級別的服務,不推薦使用。WSDL雖然具備了描述對象的能力,SOAP的S代表的也是simple,但是SOAP的使用絕對不簡單。對於習慣於面向對象編程的用戶,WSDL文件不直觀。

JSON(Javascript Object Notation)

JSON起源於弱類型語言Javascript, 它的產生來自於一種稱之爲"Associative array"的概念,其本質是就是採用"Attribute-value"的方式來描述對象。實際上在Javascript和PHP等弱類型語言中,類的描述方式就是Associative array。JSON的如下優點,使得它快速成爲最廣泛使用的序列化協議之一。

  1. 這種Associative array格式非常符合工程師對對象的理解。
  2. 它保持了XML的人眼可讀(Human-readable)的優點。
  3. 相對於XML而言,序列化後的數據更加簡潔。 來自於的以下鏈接的研究表明:XML所產生序列化之後文件的大小接近JSON的兩倍。http://www.codeproject.com/Articles/604720/JSON-vs-XML-Some-hard-numbers-about-verbosity
  4. 它具備Javascript的先天性支持,所以被廣泛應用於Web browser的應用常景中,是Ajax的事實標準協議。
  5. 與XML相比,其協議比較簡單,解析速度比較快。
  6. 鬆散的Associative array使得其具有良好的可擴展性和兼容性。

典型應用場景和非應用場景

JSON在很多應用場景中可以替代XML,更簡潔並且解析速度更快。典型應用場景包括:

  1. 公司之間傳輸數據量相對小,實時性要求相對低(例如秒級別)的服務。
  2. 基於Web browser的Ajax請求。
  3. 由於JSON具有非常強的前後兼容性,對於接口經常發生變化,並對可調式性要求高的場景,例如Mobile app與服務端的通訊。
  4. 由於JSON的典型應用場景是JSON+HTTP,適合跨防火牆訪問。總的來說,採用JSON進行序列化的額外空間開銷比較大,對於大數據量服務或持久化,這意味着巨大的內存和磁盤開銷,這種場景不適合。沒有統一可用的IDL降低了對參與方的約束,實際操作中往往只能採用文檔方式來進行約定,這可能會給調試帶來一些不便,延長開發週期。 由於JSON在一些語言中的序列化和反序列化需要採用反射機制,所以在性能要求爲ms級別,不建議使用。

IDL文件舉例

以下是UserInfo序列化之後的一個例子:

{"userid":1,"name":"messi","address":[{"city":"北京","postcode":"1000000","street":"wangjingdonglu"}]}

Thrift

Thrift是Facebook開源提供的一個高性能,輕量級RPC服務框架,其產生正是爲了滿足當前大數據量、分佈式、跨語言、跨平臺數據通訊的需求。 但是,Thrift並不僅僅是序列化協議,而是一個RPC框架。相對於JSON和XML而言,Thrift在空間開銷和解析性能上有了比較大的提升,對於對性能要求比較高的分佈式系統,它是一個優秀的RPC解決方案;但是由於Thrift的序列化被嵌入到Thrift框架裏面,Thrift框架本身並沒有透出序列化和反序列化接口,這導致其很難和其他傳輸層協議共同使用(例如HTTP)。

典型應用場景和非應用場景

對於需求爲高性能,分佈式的RPC服務,Thrift是一個優秀的解決方案。它支持衆多語言和豐富的數據類型,並對於數據字段的增刪具有較強的兼容性。所以非常適用於作爲公司內部的面向服務構建(SOA)的標準RPC框架。

不過Thrift的文檔相對比較缺乏,目前使用的羣衆基礎相對較少。另外由於其Server是基於自身的Socket服務,所以在跨防火牆訪問時,安全是一個顧慮,所以在公司間進行通訊時需要謹慎。 另外Thrift序列化之後的數據是Binary數組,不具有可讀性,調試代碼時相對困難。最後,由於Thrift的序列化和框架緊耦合,無法支持向持久層直接讀寫數據,所以不適合做數據持久化序列化協議。

IDL文件舉例

struct Address
{  1: required string city;
	2: optional string postcode;
	3: optional string street;
}  struct UserInfo
{  1: required string userid;
	2: required i32 name;
	3: optional list<address> address;
}
</address>

Protobuf

Protobuf具備了優秀的序列化協議的所需的衆多典型特徵。

  1. 標準的IDL和IDL編譯器,這使得其對工程師非常友好。
  2. 序列化數據非常簡潔,緊湊,與XML相比,其序列化之後的數據量約爲1/3到1/10。
  3. 解析速度非常快,比對應的XML快約20-100倍。
  4. 提供了非常友好的動態庫,使用非常簡介,反序列化只需要一行代碼。

Protobuf是一個純粹的展示層協議,可以和各種傳輸層協議一起使用;Protobuf的文檔也非常完善。 但是由於Protobuf產生於Google,所以目前其僅僅支持Java、C#### 典型應用場景和非應用場景 Protobuf具有廣泛的用戶基礎,空間開銷小以及高解析性能是其亮點,非常適合於公司內部的對性能要求高的RPC調用。由於Protobuf提供了標準的IDL以及對應的編譯器,其IDL文件是參與各方的非常強的業務約束,另外,Protobuf與傳輸層無關,採用HTTP具有良好的跨防火牆的訪問屬性,所以Protobuf也適用於公司間對性能要求比較高的場景。由於其解析性能高,序列化後數據量相對少,非常適合應用層對象的持久化場景。

它的主要問題在於其所支持的語言相對較少,另外由於沒有綁定的標準底層傳輸層協議,在公司間進行傳輸層協議的調試工作相對麻煩。

IDL文件舉例

message Address
{
	required string city=1;
		optional string postcode=2;
		optional string street=3;
}
message UserInfo
{
	required string userid=1;
	required string name=2;
	repeated Address address=3;
}

Avro

Avro的產生解決了JSON的冗長和沒有IDL的問題,Avro屬於Apache Hadoop的一個子項目。 Avro提供兩種序列化格式:JSON格式或者Binary格式。Binary格式在空間開銷和解析性能方面可以和Protobuf媲美,JSON格式方便測試階段的調試。 Avro支持的數據類型非常豐富,包括C#### 典型應用場景和非應用場景 Avro解析性能高並且序列化之後的數據非常簡潔,比較適合於高性能的序列化服務。

由於Avro目前非JSON格式的IDL處於實驗階段,而JSON格式的IDL對於習慣於靜態類型語言的工程師來說不直觀。

IDL文件舉例

protocol Userservice {
  record Address {
   string city;
   string postcode;
   string street;
  }  
  record UserInfo {
   string name;
   int userid;
   array<Address> address = [];
  }
}

所對應的JSON Schema格式如下:

{
  "protocol" : "Userservice",
  "namespace" : "org.apache.avro.ipc.specific",
  "version" : "1.0.5",
  "types" : [ {
	"type" : "record",
	"name" : "Address",
	"fields" : [ {
	  "name" : "city",
	  "type" : "string"
	}, {
	  "name" : "postcode",
	  "type" : "string"
	}, {
	  "name" : "street",
	  "type" : "string"
	} ]
  }, {
	"type" : "record",
	"name" : "UserInfo",
	"fields" : [ {
	  "name" : "name",
	  "type" : "string"
	}, {
	  "name" : "userid",
	  "type" : "int"
	}, {
	  "name" : "address",
	  "type" : {
		"type" : "array",
		"items" : "Address"
	  },
	  "default" : [ ]
	} ]
  } ],
  "messages" : { }
}

Benchmark以及選型建議

Benchmark

以下數據來自https://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

解析性能

序列化之空間開銷

從上圖可得出如下結論:

  1. XML序列化(Xstream)無論在性能和簡潔性上比較差;
  2. Thrift與Protobuf相比在時空開銷方面都有一定的劣勢;
  3. Protobuf和Avro在兩方面表現都非常優越。

選型建議

以上描述的五種序列化和反序列化協議都各自具有相應的特點,適用於不同的場景。

  1. 對於公司間的系統調用,如果性能要求在100ms以上的服務,基於XML的SOAP協議是一個值得考慮的方案。
  2. 基於Web browser的Ajax,以及Mobile app與服務端之間的通訊,JSON協議是首選。對於性能要求不太高,或者以動態類型語言爲主,或者傳輸數據載荷很小的的運用場景,JSON也是非常不錯的選擇。
  3. 對於調試環境比較惡劣的場景,採用JSON或XML能夠極大的提高調試效率,降低系統開發成本。
  4. 當對性能和簡潔性有極高要求的場景,Protobuf,Thrift,Avro之間具有一定的競爭關係。
  5. 對於T級別的數據的持久化應用場景,Protobuf和Avro是首要選擇。如果持久化後的數據存儲在Hadoop子項目裏,Avro會是更好的選擇。
  6. 由於Avro的設計理念偏向於動態類型語言,對於動態語言爲主的應用場景,Avro是更好的選擇。
  7. 對於持久層非Hadoop項目,以靜態類型語言爲主的應用場景,Protobuf會更符合靜態類型語言工程師的開發習慣。
  8. 如果需要提供一個完整的RPC解決方案,Thrift是一個好的選擇。
  9. 如果序列化之後需要支持不同的傳輸層協議,或者需要跨防火牆訪問的高性能場景,Protobuf可以優先考慮。


發佈了24 篇原創文章 · 獲贊 4 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章