溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何理解網(wǎng)站處理數(shù)據(jù)交換時(shí)的序列化和反序列化

發(fā)布時(shí)間:2021-09-26 13:40:46 來(lái)源:億速云 閱讀:114 作者:iii 欄目:建站服務(wù)器

本篇內(nèi)容介紹了“如何理解網(wǎng)站處理數(shù)據(jù)交換時(shí)的序列化和反序列化”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

#摘要
序列化和反序列化幾乎是工程師們每天都要面對(duì)的事情,但是要精確掌握這兩個(gè)概念并不容易:一方面,它們往往作為框架的一部分出現(xiàn)而湮沒(méi)在框架之中;另一方面,它們會(huì)以其他更容易理解的概念出現(xiàn),例如加密、持久化。然而,序列化和反序列化的選型卻是系統(tǒng)設(shè)計(jì)或重構(gòu)一個(gè)重要的環(huán)節(jié),在分布式、大數(shù)據(jù)量系統(tǒng)設(shè)計(jì)里面更為顯著。恰當(dāng)?shù)男蛄谢瘏f(xié)議不僅可以提高系統(tǒng)的通用性、強(qiáng)健性、安全性、優(yōu)化系統(tǒng)性能,而且會(huì)讓系統(tǒng)更加易于調(diào)試、便于擴(kuò)展。本文從多個(gè)角度去分析和講解“序列化和反序列化”,并對(duì)比了當(dāng)前流行的幾種序列化協(xié)議,期望對(duì)讀者做序列化選型有所幫助。

簡(jiǎn)介
文章作者服務(wù)于美團(tuán)推薦與個(gè)性化組,該組致力于為美團(tuán)用戶提供每天billion級(jí)別的高質(zhì)量個(gè)性化推薦以及排序服務(wù)。從Terabyte級(jí)別的用戶行為數(shù)據(jù),到Gigabyte級(jí)別的Deal/Poi數(shù)據(jù);從對(duì)實(shí)時(shí)性要求毫秒以內(nèi)的用戶實(shí)時(shí)地理位置數(shù)據(jù),到定期后臺(tái)job數(shù)據(jù),推薦與重排序系統(tǒng)需要多種類型的數(shù)據(jù)服務(wù)。推薦與重排序系統(tǒng)客戶包括各種內(nèi)部服務(wù)、美團(tuán)客戶端、美團(tuán)網(wǎng)站。為了提供高質(zhì)量的數(shù)據(jù)服務(wù),為了實(shí)現(xiàn)與上下游各系統(tǒng)進(jìn)行良好的對(duì)接,序列化和反序列化的選型往往是我們做系統(tǒng)設(shè)計(jì)的一個(gè)重要考慮因素。

本文內(nèi)容按如下方式組織:

第一部分給出了序列化和反序列化的定義,以及其在通訊協(xié)議中所處的位置。
第二部分從使用者的角度探討了序列化協(xié)議的一些特性。
第三部分描述在具體的實(shí)施過(guò)程中典型的序列化組件,并與數(shù)據(jù)庫(kù)組建進(jìn)行了類比。
第四部分分別講解了目前常見(jiàn)的幾種序列化協(xié)議的特性,應(yīng)用場(chǎng)景,并對(duì)相關(guān)組件進(jìn)行舉例。
最后一部分,基于各種協(xié)議的特性,以及相關(guān)benchmark數(shù)據(jù),給出了作者的技術(shù)選型建議。
#一、定義以及相關(guān)概念

互聯(lián)網(wǎng)的產(chǎn)生帶來(lái)了機(jī)器間通訊的需求,而互聯(lián)通訊的雙方需要采用約定的協(xié)議,序列化和反序列化屬于通訊協(xié)議的一部分。通訊協(xié)議往往采用分層模型,不同模型每層的功能定義以及顆粒度不同,例如:TCP/IP協(xié)議是一個(gè)四層協(xié)議,而OSI模型卻是七層協(xié)議模型。在OSI七層協(xié)議模型中展現(xiàn)層(Presentation Layer)的主要功能是把應(yīng)用層的對(duì)象轉(zhuǎn)換成一段連續(xù)的二進(jìn)制串,或者反過(guò)來(lái),把二進(jìn)制串轉(zhuǎn)換成應(yīng)用層的對(duì)象--這兩個(gè)功能就是序列化和反序列化。一般而言,TCP/IP協(xié)議的應(yīng)用層對(duì)應(yīng)與OSI七層協(xié)議模型的應(yīng)用層,展示層和會(huì)話層,所以序列化協(xié)議屬于TCP/IP協(xié)議應(yīng)用層的一部分。本文對(duì)序列化協(xié)議的講解主要基于OSI七層協(xié)議模型。

序列化: 將數(shù)據(jù)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成二進(jìn)制串的過(guò)程
反序列化:將在序列化過(guò)程中所生成的二進(jìn)制串轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)或者對(duì)象的過(guò)程
數(shù)據(jù)結(jié)構(gòu)、對(duì)象與二進(jìn)制串
不同的計(jì)算機(jī)語(yǔ)言中,數(shù)據(jù)結(jié)構(gòu),對(duì)象以及二進(jìn)制串的表示方式并不相同。

數(shù)據(jù)結(jié)構(gòu)和對(duì)象:對(duì)于類似Java這種完全面向?qū)ο蟮恼Z(yǔ)言,工程師所操作的一切都是對(duì)象(Object),來(lái)自于類的實(shí)例化。在Java語(yǔ)言中最接近數(shù)據(jù)結(jié)構(gòu)的概念,就是POJO(Plain Old Java Object)或者Javabean--那些只有setter/getter方法的類。而在C++這種半面向?qū)ο蟮恼Z(yǔ)言中,數(shù)據(jù)結(jié)構(gòu)和struct對(duì)應(yīng),對(duì)象和class對(duì)應(yīng)。

二進(jìn)制串:序列化所生成的二進(jìn)制串指的是存儲(chǔ)在內(nèi)存中的一塊數(shù)據(jù)。C++語(yǔ)言具有內(nèi)存操作符,所以二進(jìn)制串的概念容易理解,例如,C++語(yǔ)言的字符串可以直接被傳輸層使用,因?yàn)槠浔举|(zhì)上就是以'\0'結(jié)尾的存儲(chǔ)在內(nèi)存中的二進(jìn)制串。在Java語(yǔ)言里面,二進(jìn)制串的概念容易和String混淆。實(shí)際上String 是Java的一等公民,是一種特殊對(duì)象(Object)。對(duì)于跨語(yǔ)言間的通訊,序列化后的數(shù)據(jù)當(dāng)然不能是某種語(yǔ)言的特殊數(shù)據(jù)類型。二進(jìn)制串在Java里面所指的是byte[],byte是Java的8中原生數(shù)據(jù)類型之一(Primitive data types)。

#二、序列化協(xié)議特性

每種序列化協(xié)議都有優(yōu)點(diǎn)和缺點(diǎn),它們?cè)谠O(shè)計(jì)之初有自己獨(dú)特的應(yīng)用場(chǎng)景。在系統(tǒng)設(shè)計(jì)的過(guò)程中,需要考慮序列化需求的方方面面,綜合對(duì)比各種序列化協(xié)議的特性,最終給出一個(gè)折衷的方案。

通用性
通用性有兩個(gè)層面的意義:
第一、技術(shù)層面,序列化協(xié)議是否支持跨平臺(tái)、跨語(yǔ)言。如果不支持,在技術(shù)層面上的通用性就大大降低了。
第二、流行程度,序列化和反序列化需要多方參與,很少人使用的協(xié)議往往意味著昂貴的學(xué)習(xí)成本;另一方面,流行度低的協(xié)議,往往缺乏穩(wěn)定而成熟的跨語(yǔ)言、跨平臺(tái)的公共包。

強(qiáng)健性/魯棒性
以下兩個(gè)方面的原因會(huì)導(dǎo)致協(xié)議不夠強(qiáng)?。?br/>第一、成熟度不夠,一個(gè)協(xié)議從制定到實(shí)施,到最后成熟往往是一個(gè)漫長(zhǎng)的階段。協(xié)議的強(qiáng)健性依賴于大量而全面的測(cè)試,對(duì)于致力于提供高質(zhì)量服務(wù)的系統(tǒng),采用處于測(cè)試階段的序列化協(xié)議會(huì)帶來(lái)很高的風(fēng)險(xiǎn)。
第二、語(yǔ)言/平臺(tái)的不公平性。為了支持跨語(yǔ)言、跨平臺(tái)的功能,序列化協(xié)議的制定者需要做大量的工作;但是,當(dāng)所支持的語(yǔ)言或者平臺(tái)之間存在難以調(diào)和的特性的時(shí)候,協(xié)議制定者需要做一個(gè)艱難的決定--支持更多人使用的語(yǔ)言/平臺(tái),亦或支持更多的語(yǔ)言/平臺(tái)而放棄某個(gè)特性。當(dāng)協(xié)議的制定者決定為某種語(yǔ)言或平臺(tái)提供更多支持的時(shí)候,對(duì)于使用者而言,協(xié)議的強(qiáng)健性就被犧牲了。

可調(diào)試性/可讀性
序列化和反序列化的數(shù)據(jù)正確性和業(yè)務(wù)正確性的調(diào)試往往需要很長(zhǎng)的時(shí)間,良好的調(diào)試機(jī)制會(huì)大大提高開(kāi)發(fā)效率。序列化后的二進(jìn)制串往往不具備人眼可讀性,為了驗(yàn)證序列化結(jié)果的正確性,寫(xiě)入方不得同時(shí)撰寫(xiě)反序列化程序,或提供一個(gè)查詢平臺(tái)--這比較費(fèi)時(shí);另一方面,如果讀取方未能成功實(shí)現(xiàn)反序列化,這將給問(wèn)題查找?guī)?lái)了很大的挑戰(zhàn)--難以定位是由于自身的反序列化程序的bug所導(dǎo)致還是由于寫(xiě)入方序列化后的錯(cuò)誤數(shù)據(jù)所導(dǎo)致。對(duì)于跨公司間的調(diào)試,由于以下原因,問(wèn)題會(huì)顯得更嚴(yán)重:
第一、支持不到位,跨公司調(diào)試在問(wèn)題出現(xiàn)后可能得不到及時(shí)的支持,這大大延長(zhǎng)了調(diào)試周期。
第二、訪問(wèn)限制,調(diào)試階段的查詢平臺(tái)未必對(duì)外公開(kāi),這增加了讀取方的驗(yàn)證難度。

如果序列化后的數(shù)據(jù)人眼可讀,這將大大提高調(diào)試效率, XML和JSON就具有人眼可讀的優(yōu)點(diǎn)。

性能
性能包括兩個(gè)方面,時(shí)間復(fù)雜度和空間復(fù)雜度:
第一、空間開(kāi)銷(Verbosity), 序列化需要在原有的數(shù)據(jù)上加上描述字段,以為反序列化解析之用。如果序列化過(guò)程引入的額外開(kāi)銷過(guò)高,可能會(huì)導(dǎo)致過(guò)大的網(wǎng)絡(luò),磁盤(pán)等各方面的壓力。對(duì)于海量分布式存儲(chǔ)系統(tǒng),數(shù)據(jù)量往往以TB為單位,巨大的的額外空間開(kāi)銷意味著高昂的成本。
第二、時(shí)間開(kāi)銷(Complexity),復(fù)雜的序列化協(xié)議會(huì)導(dǎo)致較長(zhǎng)的解析時(shí)間,這可能會(huì)使得序列化和反序列化階段成為整個(gè)系統(tǒng)的瓶頸。

可擴(kuò)展性/兼容性
移動(dòng)互聯(lián)時(shí)代,業(yè)務(wù)系統(tǒng)需求的更新周期變得更快,新的需求不斷涌現(xiàn),而老的系統(tǒng)還是需要繼續(xù)維護(hù)。如果序列化協(xié)議具有良好的可擴(kuò)展性,支持自動(dòng)增加新的業(yè)務(wù)字段,而不影響老的服務(wù),這將大大提供系統(tǒng)的靈活度。

安全性/訪問(wèn)限制
在序列化選型的過(guò)程中,安全性的考慮往往發(fā)生在跨局域網(wǎng)訪問(wèn)的場(chǎng)景。當(dāng)通訊發(fā)生在公司之間或者跨機(jī)房的時(shí)候,出于安全的考慮,對(duì)于跨局域網(wǎng)的訪問(wèn)往往被限制為基于HTTP/HTTPS的80和443端口。如果使用的序列化協(xié)議沒(méi)有兼容而成熟的HTTP傳輸層框架支持,可能會(huì)導(dǎo)致以下三種結(jié)果之一:
第一、因?yàn)樵L問(wèn)限制而降低服務(wù)可用性。
第二、被迫重新實(shí)現(xiàn)安全協(xié)議而導(dǎo)致實(shí)施成本大大提高。
第三、開(kāi)放更多的防火墻端口和協(xié)議訪問(wèn),而犧牲安全性。

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

典型的序列化和反序列化過(guò)程往往需要如下組件:

IDL(Interface description language)文件:參與通訊的各方需要對(duì)通訊的內(nèi)容需要做相關(guān)的約定(Specifications)。為了建立一個(gè)與語(yǔ)言和平臺(tái)無(wú)關(guān)的約定,這個(gè)約定需要采用與具體開(kāi)發(fā)語(yǔ)言、平臺(tái)無(wú)關(guān)的語(yǔ)言來(lái)進(jìn)行描述。這種語(yǔ)言被稱為接口描述語(yǔ)言(IDL),采用IDL撰寫(xiě)的協(xié)議約定稱之為IDL文件。
IDL Compiler:IDL文件中約定的內(nèi)容為了在各語(yǔ)言和平臺(tái)可見(jiàn),需要有一個(gè)編譯器,將IDL文件轉(zhuǎn)換成各語(yǔ)言對(duì)應(yīng)的動(dòng)態(tài)庫(kù)。
Stub/Skeleton Lib:負(fù)責(zé)序列化和反序列化的工作代碼。Stub是一段部署在分布式系統(tǒng)客戶端的代碼,一方面接收應(yīng)用層的參數(shù),并對(duì)其序列化后通過(guò)底層協(xié)議棧發(fā)送到服務(wù)端,另一方面接收服務(wù)端序列化后的結(jié)果數(shù)據(jù),反序列化后交給客戶端應(yīng)用層;Skeleton部署在服務(wù)端,其功能與Stub相反,從傳輸層接收序列化參數(shù),反序列化后交給服務(wù)端應(yīng)用層,并將應(yīng)用層的執(zhí)行結(jié)果序列化后最終傳送給客戶端Stub。
Client/Server:指的是應(yīng)用層程序代碼,他們面對(duì)的是IDL所生存的特定語(yǔ)言的class或struct。
底層協(xié)議棧和互聯(lián)網(wǎng):序列化之后的數(shù)據(jù)通過(guò)底層的傳輸層、網(wǎng)絡(luò)層、鏈路層以及物理層協(xié)議轉(zhuǎn)換成數(shù)字信號(hào)在互聯(lián)網(wǎng)中傳遞。
如何理解網(wǎng)站處理數(shù)據(jù)交換時(shí)的序列化和反序列化

序列化組件與數(shù)據(jù)庫(kù)訪問(wèn)組件的對(duì)比
數(shù)據(jù)庫(kù)訪問(wèn)對(duì)于很多工程師來(lái)說(shuō)相對(duì)熟悉,所用到的組件也相對(duì)容易理解。下表類比了序列化過(guò)程中用到的部分組件和數(shù)據(jù)庫(kù)訪問(wèn)組件的對(duì)應(yīng)關(guān)系,以便于大家更好的把握序列化相關(guān)組件的概念。
如何理解網(wǎng)站處理數(shù)據(jù)交換時(shí)的序列化和反序列化

#四、幾種常見(jiàn)的序列化和反序列化協(xié)議

互聯(lián)網(wǎng)早期的序列化協(xié)議主要有COM和CORBA。

COM主要用于Windows平臺(tái),并沒(méi)有真正實(shí)現(xiàn)跨平臺(tái),另外COM的序列化的原理利用了編譯器中虛表,使得其學(xué)習(xí)成本巨大(想一下這個(gè)場(chǎng)景, 工程師需要是簡(jiǎn)單的序列化協(xié)議,但卻要先掌握語(yǔ)言編譯器)。由于序列化的數(shù)據(jù)與編譯器緊耦合,擴(kuò)展屬性非常麻煩。

CORBA是早期比較好的實(shí)現(xiàn)了跨平臺(tái),跨語(yǔ)言的序列化協(xié)議。COBRA的主要問(wèn)題是參與方過(guò)多帶來(lái)的版本過(guò)多,版本之間兼容性較差,以及使用復(fù)雜晦澀。這些政治經(jīng)濟(jì),技術(shù)實(shí)現(xiàn)以及早期設(shè)計(jì)不成熟的問(wèn)題,最終導(dǎo)致COBRA的漸漸消亡。J2SE 1.3之后的版本提供了基于CORBA協(xié)議的RMI-IIOP技術(shù),這使得Java開(kāi)發(fā)者可以采用純粹的Java語(yǔ)言進(jìn)行CORBA的開(kāi)發(fā)。

這里主要介紹和對(duì)比幾種當(dāng)下比較流行的序列化協(xié)議,包括XML、JSON、Protobuf、Thrift和Avro。

一個(gè)例子
如前所述,序列化和反序列化的出現(xiàn)往往晦澀而隱蔽,與其他概念之間往往相互包容。為了更好了讓大家理解序列化和反序列化的相關(guān)概念在每種協(xié)議里面的具體實(shí)現(xiàn),我們將一個(gè)例子穿插在各種序列化協(xié)議講解中。在該例子中,我們希望將一個(gè)用戶信息在多個(gè)系統(tǒng)里面進(jìn)行傳遞;在應(yīng)用層,如果采用Java語(yǔ)言,所面對(duì)的類對(duì)象如下所示:

Java Code復(fù)制內(nèi)容到剪貼板

  1. class Address   

  2. {   

  3.     private String city;   

  4.     private String postcode;   

  5.     private String street;   

  6. }   

  7. public class UserInfo   

  8. {   

  9.     private Integer userid;   

  10.     private String name;   

  11.     private List<Address> address;   

  12. }  

XML&SOAP
XML是一種常用的序列化和反序列化協(xié)議,具有跨機(jī)器,跨語(yǔ)言等優(yōu)點(diǎn)。 XML歷史悠久,其1.0版本早在1998年就形成標(biāo)準(zhǔn),并被廣泛使用至今。XML的最初產(chǎn)生目標(biāo)是對(duì)互聯(lián)網(wǎng)文檔(Document)進(jìn)行標(biāo)記,所以它的設(shè)計(jì)理念中就包含了對(duì)于人和機(jī)器都具備可讀性。 但是,當(dāng)這種標(biāo)記文檔的設(shè)計(jì)被用來(lái)序列化對(duì)象的時(shí)候,就顯得冗長(zhǎng)而復(fù)雜(Verbose and Complex)。 XML本質(zhì)上是一種描述語(yǔ)言,并且具有自我描述(Self-describing)的屬性,所以XML自身就被用于XML序列化的IDL。 標(biāo)準(zhǔn)的XML描述格式有兩種:DTD(Document Type Definition)和XSD(XML Schema Definition)。作為一種人眼可讀(Human-readable)的描述語(yǔ)言,XML被廣泛使用在配置文件中,例如O/R mapping、 Spring Bean Configuration File 等。

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

自我描述與遞歸
SOAP是一種采用XML進(jìn)行序列化和反序列化的協(xié)議,它的IDL是WSDL. 而WSDL的描述文件是XSD,而XSD自身是一種XML文件。 這里產(chǎn)生了一種有趣的在數(shù)學(xué)上稱之為“遞歸”的問(wèn)題,這種現(xiàn)象往往發(fā)生在一些具有自我屬性(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:complexType>


典型應(yīng)用場(chǎng)景和非應(yīng)用場(chǎng)景
SOAP協(xié)議具有廣泛的群眾基礎(chǔ),基于HTTP的傳輸協(xié)議使得其在穿越防火墻時(shí)具有良好安全特性,XML所具有的人眼可讀(Human-readable)特性使得其具有出眾的可調(diào)試性,互聯(lián)網(wǎng)帶寬的日益劇增也大大彌補(bǔ)了其空間開(kāi)銷大(Verbose)的缺點(diǎn)。對(duì)于在公司之間傳輸數(shù)據(jù)量相對(duì)小或者實(shí)時(shí)性要求相對(duì)低(例如秒級(jí)別)的服務(wù)是一個(gè)好的選擇。

由于XML的額外空間開(kāi)銷大,序列化之后的數(shù)據(jù)量劇增,對(duì)于數(shù)據(jù)量巨大序列持久化應(yīng)用常景,這意味著巨大的內(nèi)存和磁盤(pán)開(kāi)銷,不太適合XML。另外,XML的序列化和反序列化的空間和時(shí)間開(kāi)銷都比較大,對(duì)于對(duì)性能要求在ms級(jí)別的服務(wù),不推薦使用。WSDL雖然具備了描述對(duì)象的能力,SOAP的S代表的也是simple,但是SOAP的使用絕對(duì)不簡(jiǎn)單。對(duì)于習(xí)慣于面向?qū)ο缶幊痰挠脩?,WSDL文件不直觀。

JSON(Javascript Object Notation)
JSON起源于弱類型語(yǔ)言Javascript, 它的產(chǎn)生來(lái)自于一種稱之為"Associative array"的概念,其本質(zhì)是就是采用"Attribute-value"的方式來(lái)描述對(duì)象。實(shí)際上在Javascript和PHP等弱類型語(yǔ)言中,類的描述方式就是Associative array。JSON的如下優(yōu)點(diǎn),使得它快速成為最廣泛使用的序列化協(xié)議之一:
1、這種Associative array格式非常符合工程師對(duì)對(duì)象的理解。
2、它保持了XML的人眼可讀(Human-readable)的優(yōu)點(diǎn)。
3、相對(duì)于XML而言,序列化后的數(shù)據(jù)更加簡(jiǎn)潔。 來(lái)自于的以下鏈接的研究表明:XML所產(chǎn)生序列化之后文件的大小接近JSON的兩倍。http://www.codeproject.com/Articles/604720/JSON-vs-XML-Some-hard-numbers-about-verbosity
4、它具備Javascript的先天性支持,所以被廣泛應(yīng)用于Web browser的應(yīng)用常景中,是Ajax的事實(shí)標(biāo)準(zhǔn)協(xié)議。
5、與XML相比,其協(xié)議比較簡(jiǎn)單,解析速度比較快。
6、松散的Associative array使得其具有良好的可擴(kuò)展性和兼容性。

IDL悖論
JSON實(shí)在是太簡(jiǎn)單了,或者說(shuō)太像各種語(yǔ)言里面的類了,所以采用JSON進(jìn)行序列化不需要IDL。這實(shí)在是太神奇了,存在一種天然的序列化協(xié)議,自身就實(shí)現(xiàn)了跨語(yǔ)言和跨平臺(tái)。然而事實(shí)沒(méi)有那么神奇,之所以產(chǎn)生這種假象,來(lái)自于兩個(gè)原因:
第一、Associative array在弱類型語(yǔ)言里面就是類的概念,在PHP和Javascript里面Associative array就是其class的實(shí)際實(shí)現(xiàn)方式,所以在這些弱類型語(yǔ)言里面,JSON得到了非常良好的支持。
第二、IDL的目的是撰寫(xiě)IDL文件,而IDL文件被IDL Compiler編譯后能夠產(chǎn)生一些代碼(Stub/Skeleton),而這些代碼是真正負(fù)責(zé)相應(yīng)的序列化和反序列化工作的組件。 但是由于Associative array和一般語(yǔ)言里面的class太像了,他們之間形成了一一對(duì)應(yīng)關(guān)系,這就使得我們可以采用一套標(biāo)準(zhǔn)的代碼進(jìn)行相應(yīng)的轉(zhuǎn)化。對(duì)于自身支持Associative array的弱類型語(yǔ)言,語(yǔ)言自身就具備操作JSON序列化后的數(shù)據(jù)的能力;對(duì)于Java這強(qiáng)類型語(yǔ)言,可以采用反射的方式統(tǒng)一解決,例如Google提供的Gson。

典型應(yīng)用場(chǎng)景和非應(yīng)用場(chǎng)景
JSON在很多應(yīng)用場(chǎng)景中可以替代XML,更簡(jiǎn)潔并且解析速度更快。典型應(yīng)用場(chǎng)景包括:
1、公司之間傳輸數(shù)據(jù)量相對(duì)小,實(shí)時(shí)性要求相對(duì)低(例如秒級(jí)別)的服務(wù)。
2、基于Web browser的Ajax請(qǐng)求。
3、由于JSON具有非常強(qiáng)的前后兼容性,對(duì)于接口經(jīng)常發(fā)生變化,并對(duì)可調(diào)式性要求高的場(chǎng)景,例如Mobile app與服務(wù)端的通訊。
4、由于JSON的典型應(yīng)用場(chǎng)景是JSON+HTTP,適合跨防火墻訪問(wèn)。

總的來(lái)說(shuō),采用JSON進(jìn)行序列化的額外空間開(kāi)銷比較大,對(duì)于大數(shù)據(jù)量服務(wù)或持久化,這意味著巨大的內(nèi)存和磁盤(pán)開(kāi)銷,這種場(chǎng)景不適合。沒(méi)有統(tǒng)一可用的IDL降低了對(duì)參與方的約束,實(shí)際操作中往往只能采用文檔方式來(lái)進(jìn)行約定,這可能會(huì)給調(diào)試帶來(lái)一些不便,延長(zhǎng)開(kāi)發(fā)周期。 由于JSON在一些語(yǔ)言中的序列化和反序列化需要采用反射機(jī)制,所以在性能要求為ms級(jí)別,不建議使用。

IDL文件舉例
以下是UserInfo序列化之后的一個(gè)例子:

代碼如下:

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


Thrift
Thrift是Facebook開(kāi)源提供的一個(gè)高性能,輕量級(jí)RPC服務(wù)框架,其產(chǎn)生正是為了滿足當(dāng)前大數(shù)據(jù)量、分布式、跨語(yǔ)言、跨平臺(tái)數(shù)據(jù)通訊的需求。 但是,Thrift并不僅僅是序列化協(xié)議,而是一個(gè)RPC框架。相對(duì)于JSON和XML而言,Thrift在空間開(kāi)銷和解析性能上有了比較大的提升,對(duì)于對(duì)性能要求比較高的分布式系統(tǒng),它是一個(gè)優(yōu)秀的RPC解決方案;但是由于Thrift的序列化被嵌入到Thrift框架里面,Thrift框架本身并沒(méi)有透出序列化和反序列化接口,這導(dǎo)致其很難和其他傳輸層協(xié)議共同使用(例如HTTP)。

典型應(yīng)用場(chǎng)景和非應(yīng)用場(chǎng)景
對(duì)于需求為高性能,分布式的RPC服務(wù),Thrift是一個(gè)優(yōu)秀的解決方案。它支持眾多語(yǔ)言和豐富的數(shù)據(jù)類型,并對(duì)于數(shù)據(jù)字段的增刪具有較強(qiáng)的兼容性。所以非常適用于作為公司內(nèi)部的面向服務(wù)構(gòu)建(SOA)的標(biāo)準(zhǔn)RPC框架。

不過(guò)Thrift的文檔相對(duì)比較缺乏,目前使用的群眾基礎(chǔ)相對(duì)較少。另外由于其Server是基于自身的Socket服務(wù),所以在跨防火墻訪問(wèn)時(shí),安全是一個(gè)顧慮,所以在公司間進(jìn)行通訊時(shí)需要謹(jǐn)慎。 另外Thrift序列化之后的數(shù)據(jù)是Binary數(shù)組,不具有可讀性,調(diào)試代碼時(shí)相對(duì)困難。最后,由于Thrift的序列化和框架緊耦合,無(wú)法支持向持久層直接讀寫(xiě)數(shù)據(jù),所以不適合做數(shù)據(jù)持久化序列化協(xié)議。

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具備了優(yōu)秀的序列化協(xié)議的所需的眾多典型特征:
1、標(biāo)準(zhǔn)的IDL和IDL編譯器,這使得其對(duì)工程師非常友好。
2、序列化數(shù)據(jù)非常簡(jiǎn)潔,緊湊,與XML相比,其序列化之后的數(shù)據(jù)量約為1/3到1/10。
3、解析速度非??欤葘?duì)應(yīng)的XML快約20-100倍。
4、提供了非常友好的動(dòng)態(tài)庫(kù),使用非常簡(jiǎn)介,反序列化只需要一行代碼。

Protobuf是一個(gè)純粹的展示層協(xié)議,可以和各種傳輸層協(xié)議一起使用;Protobuf的文檔也非常完善。 但是由于Protobuf產(chǎn)生于Google,所以目前其僅僅支持Java、C++、Python三種語(yǔ)言。另外Protobuf支持的數(shù)據(jù)類型相對(duì)較少,不支持常量類型。由于其設(shè)計(jì)的理念是純粹的展現(xiàn)層協(xié)議(Presentation Layer),目前并沒(méi)有一個(gè)專門(mén)支持Protobuf的RPC框架。

典型應(yīng)用場(chǎng)景和非應(yīng)用場(chǎng)景
Protobuf具有廣泛的用戶基礎(chǔ),空間開(kāi)銷小以及高解析性能是其亮點(diǎn),非常適合于公司內(nèi)部的對(duì)性能要求高的RPC調(diào)用。由于Protobuf提供了標(biāo)準(zhǔn)的IDL以及對(duì)應(yīng)的編譯器,其IDL文件是參與各方的非常強(qiáng)的業(yè)務(wù)約束,另外,Protobuf與傳輸層無(wú)關(guān),采用HTTP具有良好的跨防火墻的訪問(wèn)屬性,所以Protobuf也適用于公司間對(duì)性能要求比較高的場(chǎng)景。由于其解析性能高,序列化后數(shù)據(jù)量相對(duì)少,非常適合應(yīng)用層對(duì)象的持久化場(chǎng)景。

它的主要問(wèn)題在于其所支持的語(yǔ)言相對(duì)較少,另外由于沒(méi)有綁定的標(biāo)準(zhǔn)底層傳輸層協(xié)議,在公司間進(jìn)行傳輸層協(xié)議的調(diào)試工作相對(duì)麻煩。

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的產(chǎn)生解決了JSON的冗長(zhǎng)和沒(méi)有IDL的問(wèn)題,Avro屬于Apache Hadoop的一個(gè)子項(xiàng)目。 Avro提供兩種序列化格式:JSON格式或者Binary格式。Binary格式在空間開(kāi)銷和解析性能方面可以和Protobuf媲美,JSON格式方便測(cè)試階段的調(diào)試。 Avro支持的數(shù)據(jù)類型非常豐富,包括C++語(yǔ)言里面的union類型。Avro支持JSON格式的IDL和類似于Thrift和Protobuf的IDL(實(shí)驗(yàn)階段),這兩者之間可以互轉(zhuǎn)。Schema可以在傳輸數(shù)據(jù)的同時(shí)發(fā)送,加上JSON的自我描述屬性,這使得Avro非常適合動(dòng)態(tài)類型語(yǔ)言。 Avro在做文件持久化的時(shí)候,一般會(huì)和Schema一起存儲(chǔ),所以Avro序列化文件自身具有自我描述屬性,所以非常適合于做Hive、Pig和MapReduce的持久化數(shù)據(jù)格式。對(duì)于不同版本的Schema,在進(jìn)行RPC調(diào)用的時(shí)候,服務(wù)端和客戶端可以在握手階段對(duì)Schema進(jìn)行互相確認(rèn),大大提高了最終的數(shù)據(jù)解析速度。

典型應(yīng)用場(chǎng)景和非應(yīng)用場(chǎng)景
Avro解析性能高并且序列化之后的數(shù)據(jù)非常簡(jiǎn)潔,比較適合于高性能的序列化服務(wù)。

由于Avro目前非JSON格式的IDL處于實(shí)驗(yàn)階段,而JSON格式的IDL對(duì)于習(xí)慣于靜態(tài)類型語(yǔ)言的工程師來(lái)說(shuō)不直觀。

IDL文件舉例

代碼如下:


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


所對(duì)應(yīng)的JSON Schema格式如下:

JavaScript Code復(fù)制內(nèi)容到剪貼板

  1. {   

  2.   "protocol" : "Userservice",   

  3.   "namespace" : "org.apache.avro.ipc.specific",   

  4.   "version" : "1.0.5",   

  5.   "types" : [ {   

  6.     "type" : "record",   

  7.     "name" : "Address",   

  8.     "fields" : [ {   

  9.       "name" : "city",   

  10.       "type" : "string"  

  11.     }, {   

  12.       "name" : "postcode",   

  13.       "type" : "string"  

  14.     }, {   

  15.       "name" : "street",   

  16.       "type" : "string"  

  17.     } ]   

  18.   }, {   

  19.     "type" : "record",   

  20.     "name" : "UserInfo",   

  21.     "fields" : [ {   

  22.       "name" : "name",   

  23.       "type" : "string"  

  24.     }, {   

  25.       "name" : "userid",   

  26.       "type" : "int"  

  27.     }, {   

  28.       "name" : "address",   

  29.       "type" : {   

  30.         "type" : "array",   

  31.         "items" : "Address"  

  32.       },   

  33.       "default" : [ ]   

  34.     } ]   

  35.   } ],   

  36.   "messages" : { }   

  37. }  

#五、Benchmark以及選型建議

##Benchmark
以下數(shù)據(jù)來(lái)自https://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

解析性能
如何理解網(wǎng)站處理數(shù)據(jù)交換時(shí)的序列化和反序列化

序列化之空間開(kāi)銷
如何理解網(wǎng)站處理數(shù)據(jù)交換時(shí)的序列化和反序列化

從上圖可得出如下結(jié)論:
1、XML序列化(Xstream)無(wú)論在性能和簡(jiǎn)潔性上比較差。
2、Thrift與Protobuf相比在時(shí)空開(kāi)銷方面都有一定的劣勢(shì)。
3、Protobuf和Avro在兩方面表現(xiàn)都非常優(yōu)越。

選型建議
以上描述的五種序列化和反序列化協(xié)議都各自具有相應(yīng)的特點(diǎn),適用于不同的場(chǎng)景:
1、對(duì)于公司間的系統(tǒng)調(diào)用,如果性能要求在100ms以上的服務(wù),基于XML的SOAP協(xié)議是一個(gè)值得考慮的方案。
2、基于Web browser的Ajax,以及Mobile app與服務(wù)端之間的通訊,JSON協(xié)議是首選。對(duì)于性能要求不太高,或者以動(dòng)態(tài)類型語(yǔ)言為主,或者傳輸數(shù)據(jù)載荷很小的的運(yùn)用場(chǎng)景,JSON也是非常不錯(cuò)的選擇。
3、對(duì)于調(diào)試環(huán)境比較惡劣的場(chǎng)景,采用JSON或XML能夠極大的提高調(diào)試效率,降低系統(tǒng)開(kāi)發(fā)成本。
4、當(dāng)對(duì)性能和簡(jiǎn)潔性有極高要求的場(chǎng)景,Protobuf,Thrift,Avro之間具有一定的競(jìng)爭(zhēng)關(guān)系。
5、對(duì)于T級(jí)別的數(shù)據(jù)的持久化應(yīng)用場(chǎng)景,Protobuf和Avro是首要選擇。如果持久化后的數(shù)據(jù)存儲(chǔ)在Hadoop子項(xiàng)目里,Avro會(huì)是更好的選擇。
6、由于Avro的設(shè)計(jì)理念偏向于動(dòng)態(tài)類型語(yǔ)言,對(duì)于動(dòng)態(tài)語(yǔ)言為主的應(yīng)用場(chǎng)景,Avro是更好的選擇。
7、對(duì)于持久層非Hadoop項(xiàng)目,以靜態(tài)類型語(yǔ)言為主的應(yīng)用場(chǎng)景,Protobuf會(huì)更符合靜態(tài)類型語(yǔ)言工程師的開(kāi)發(fā)習(xí)慣。
8、如果需要提供一個(gè)完整的RPC解決方案,Thrift是一個(gè)好的選擇。
9、如果序列化之后需要支持不同的傳輸層協(xié)議,或者需要跨防火墻訪問(wèn)的高性能場(chǎng)景,Protobuf可以優(yōu)先考慮。

“如何理解網(wǎng)站處理數(shù)據(jù)交換時(shí)的序列化和反序列化”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI