天天看點

序列化和反序列化-劉丁接口描述語言(IDL)序列化元件與資料庫通路元件的對比參考文獻:

#一、定義以及相關概念

網際網路的産生帶來了機器間通訊的需求,而互聯通訊的雙方需要采用約定的協定,序列化和反序列化屬于通訊協定的一部分。通訊協定往往采用分層模型,不同模型每層的功能定義以及顆粒度不同,例如:TCP/IP協定是一個四層協定,而OSI模型卻是七層協定模型。在OSI七層協定模型中展現層(Presentation Layer)的主要功能是把應用層的對象轉換成一段連續的二進制串,或者反過來,把二進制串轉換成應用層的對象--這兩個功能就是序列化和反序列化。

一般而言,TCP/IP協定的應用層對應與OSI七層協定模型的應用層,展示層和會話層,是以序列化協定屬于TCP/IP協定應用層的一部分。

本文對序列化協定的講解主要基于OSI七層協定模型。

  • 序列化: 将資料結構或對象轉換成二進制串的過程
  • 反序列化:将在序列化過程中所生成的二進制串轉換成資料結構或者對象的過程

資料結構、對象與二進制串

不同的計算機語言中,資料結構,對象以及二進制串的表示方式并不相同。

資料結構和對象:對于類似Java這種完全面向對象的語言,工程師所操作的一切都是對象(Object),來自于類的執行個體化。在Java語言中最接近資料結構的概念,就是POJO(Plain Old Java Object)或者Javabean--那些隻有setter/getter方法的類。而在C++這種半面向對象的語言中,資料結構和struct對應,對象和class對應。

二進制串:序列化所生成的二進制串指的是存儲在記憶體中的一塊資料。C++語言具有記憶體操作符,是以二進制串的概念容易了解,例如,C++語言的字元串可以直接被傳輸層使用,因為其本質上就是以'\0'結尾的存儲在記憶體中的二進制串。在Java語言裡面,二進制串的概念容易和String混淆。實際上String 是Java的一等公民,是一種特殊對象(Object)。對于跨語言間的通訊,序列化後的資料當然不能是某種語言的特殊資料類型。二進制串在Java裡面所指的是byte[],byte是Java的8中原生資料類型之一(Primitive data types)。

#二、序列化協定特性

略 見 序列化和反序列化 - - https://tech.meituan.com/serialization_vs_deserialization.html?utm_source=tuicool?utm_source=tuicool

#三、序列化和反序列化的元件

序列化和反序列化-劉丁接口描述語言(IDL)序列化元件與資料庫通路元件的對比參考文獻:

接口描述語言(IDL)

IDL語言全稱是Interactive Data Language,對于老一些的計算機專家來說,這個縮寫還有另外幾個意思,就是Interface description language,接口描述語言,是CORBA規範的一部分。在搜尋資料的時候,要适當加以确認。

IDL(Interface Description Language),它是一種描述語言,也是一個中間語言,IDL一個使命就是規範和限制,就像前面提到,規範使用類型,提供跨語言特性。通過工具分析idl檔案,生成各種語言代碼

Gencpp.exe sample.idl 輸出 sample.cpp sample.h

Genphp.exe sample.idl 輸出 sample.php

Genjava.exe sample.idl 輸出 sample.java

序列化元件與資料庫通路元件的對比

資料庫通路對于很多工程師來說相對熟悉,所用到的元件也相對容易了解。下表類比了序列化過程中用到的部分元件和資料庫通路元件的對應關系,以便于大家更好的把握序列化相關元件的概念。

序列化元件 資料庫元件 說明
IDL DDL 用于建表或者模型的語言
DL file DB Schema 表建立檔案或模型檔案
Stub/Skeleton lib O/R mapping 将class和Table或者資料模型進行映射

#四、幾種常見的序列化和反序列化 協定

網際網路早期的序列化協定主要有COM和CORBA

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

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

當下比較流行的序列化協定,包括XML、JSON、Protobuf、Thrift和Avro

XML&SOAP

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

SOAP協定具有廣泛的群衆基礎,基于HTTP的傳輸協定使得其在穿越防火牆時具有良好安全特性,XML所具有的人眼可讀(Human-readable)特性使得其具有出衆的可調試性,網際網路帶寬的日益劇增也大大彌補了其空間開銷大(Verbose)的缺點。對于在公司之間傳輸資料量相對小或者實時性要求相對低(例如秒級别)的服務是一個好的選擇。

由于XML的額外空間開銷大,序列化之後的資料量劇增,對于資料量巨大序列持久化應用常景,這意味着巨大的記憶體和磁盤開銷,不太适合XML。另外,XML的序列化和反序列化的空間和時間開銷都比較大,對于對性能要求在ms級别的服務,不推薦使用。WSDL雖然具備了描述對象的能力,SOAP的S代表的也是simple,但是SOAP的使用絕對不簡單。對于習慣于面向對象程式設計的使用者,WSDL檔案不直覺。

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:complexType>
           

JSON(Javascript Object Notation)

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

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;
}      

Protobuf

Protobuf具備了優秀的序列化協定的所需的衆多典型特征:

1、标準的IDL和IDL編譯器,這使得其對工程師非常友好。

2、序列化資料非常簡潔,緊湊,與XML相比,其序列化之後的資料量約為1/3到1/10。

3、解析速度非常快,比對應的XML快約20-100倍。

4、提供了非常友好的動态庫,使用非常簡介,反序列化隻需要一行代碼。

Protobuf是一個純粹的展示層協定,可以和各種傳輸層協定一起使用;Protobuf的文檔也非常完善。 但是由于Protobuf産生于Google,是以目前其僅僅支援Java、C++、Python三種語言。另外Protobuf支援的資料類型相對較少,不支援常量類型。由于其設計的理念是純粹的展現層協定(Presentation Layer),目前并沒有一個專門支援Protobuf的RPC架構。

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

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++語言裡面的union類型。Avro支援JSON格式的IDL和類似于Thrift和Protobuf的IDL(實驗階段),這兩者之間可以互轉。Schema可以在傳輸資料的同時發送,加上JSON的自我描述屬性,這使得Avro非常适合動态類型語言。 Avro在做檔案持久化的時候,一般會和Schema一起存儲,是以Avro序列化檔案自身具有自我描述屬性,是以非常适合于做Hive、Pig和MapReduce的持久化資料格式。對于不同版本的Schema,在進行RPC調用的時候,服務端和用戶端可以在握手階段對Schema進行互相确認,大大提高了最終的資料解析速度。

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

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

解析性能

序列化和反序列化-劉丁接口描述語言(IDL)序列化元件與資料庫通路元件的對比參考文獻:

序列化之空間開銷

序列化和反序列化-劉丁接口描述語言(IDL)序列化元件與資料庫通路元件的對比參考文獻:

從上圖可得出如下結論:

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可以優先考慮。

參考文獻:

http://www.codeproject.com/Articles/604720/JSON-vs-XML-Some-hard-numbers-about-verbosity

https://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

http://en.wikipedia.org/wiki/Serialization

http://en.wikipedia.org/wiki/Soap

http://en.wikipedia.org/wiki/XML

http://en.wikipedia.org/wiki/JSON

http://avro.apache.org/

http://www.oracle.com/technetwork/java/rmi-iiop-139743.html

參考:

序列化和反序列化 - 美團點評技術團隊 -  https://tech.meituan.com/serialization_vs_deserialization.html?utm_source=tuicool?utm_source=tuicool

通信協定之序列化 -  https://mp.weixin.qq.com/s?src=3&timestamp=1499594507&ver=1&signature=kIPbRVrJCHVJGQ9JKVjVN68SPBrvMgbQHFUQI4tNlT9zNV4b0WLnbo5nngOVx2xm0JcILFEM1XzfX7XFognvlijLppizp0lD4M*NiwoEG9cIEx1ozj0Jz0u2zcw65nyEU7r1Btwg8AuMlqSckFBBzTW0Oc4MTMhG6Vz2Al0Ea1E=

小林教IDL【§1. IDL簡介 -  https://mp.weixin.qq.com/s?src=3&timestamp=1499594507&ver=1&signature=dJFkS3xbR1f05G1tyzVZAjZPlFLta1now9tRWp7IO-9pRzKN2EpMWO61Uv9hJUuanXwdBmxtJsZ0qbZcdv*kWwZrduYtmLxbdGKXHm9fsdm8XYSnt6L4ItWF-h*uxARrIb3pUfdK3g6T9gOr5Y9xHKSQXMHgCQoBSwt4W5zSZ7E=

轉載于:https://www.cnblogs.com/liango/p/7143848.html