Avro總結(RPC/序列化)

Avro(讀音相似於[ævrə])是Hadoop的一個子項目,由Hadoop的創始人Doug Cutting(也是Lucene,Nutch等項目的創始人,膜拜)牽頭開發,當前最新版本1.3.3。Avro是一個數據序列化系統,設計用於支持大批量數據交換的應用。它的主要特色有:支持二進制序列化方式,能夠便捷,快速地處理大量數據;動態語言友好,Avro提供的機制使動態語言能夠方便地處理Avro數據。 

     當前市場上有不少相似的序列化系統,如Google的Protocol Buffers, Facebook的Thrift。這些系統反響良好,徹底能夠知足普通應用的需求。針對重複開發的疑惑,Doug Cutting撰文解釋道:Hadoop現存的RPC系統遇到一些問題,如性能瓶頸(當前採用IPC系統,它使用Java自帶的DataOutputStream和DataInputStream);須要服務器端和客戶端必須運行相同版本的Hadoop;只能使用Java開發等。但現存的這些序列化系統自身也有毛病,以Protocol Buffers爲例,它須要用戶先定義數據結構,而後根據這個數據結構生成代碼,再組裝數據。若是須要操做多個數據源的數據集,那麼須要定義多套數據結構並重復執行屢次上面的流程,這樣就不能對任意數據集作統一處理。其次,對於Hadoop中Hive和Pig這樣的腳本系統來講,使用代碼生成是不合理的。而且Protocol Buffers在序列化時考慮到數據定義與數據可能不徹底匹配,在數據中添加註解,這會讓數據變得龐大並拖慢處理速度。其它序列化系統有如Protocol Buffers相似的問題。因此爲了Hadoop的前途考慮,Doug Cutting主導開發一套全新的序列化系統,這就是Avro,於09年加入Hadoop項目族中。 

     上面經過與Protocol Buffers的對比,大體清楚了Avro的特長。下面着重關注Avro的細節部分。 

     Avro依賴模式(Schema)來實現數據結構定義。能夠把模式理解爲Java的類,它定義每一個實例的結構,能夠包含哪些屬性。能夠根據類來產生任意多個實例對象。對實例序列化操做時必須須要知道它的基本結構,也就須要參考類的信息。這裏,根據模式產生的Avro對象相似於類的實例對象。每次序列化/反序列化時都須要知道模式的具體結構。因此,在Avro可用的一些場景下,如文件存儲或是網絡通訊,都須要模式與數據同時存在。Avro數據以模式來讀和寫(文件或是網絡),而且寫入的數據都不須要加入其它標識,這樣序列化時速度快且結果內容少。因爲程序能夠直接根據模式來處理數據,因此Avro更適合於腳本語言的發揮。 

     Avro的模式主要由JSON對象來表示,它可能會有一些特定的屬性,用來描述某種類型(Type)的不一樣形式。Avro支持八種基本類型(Primitive Type)和六種混合類型(Complex Type)。基本類型能夠由JSON字符串來表示。每種不一樣的混合類型有不一樣的屬性(Attribute)來定義,有些屬性是必須的,有些是可選的,若是須要的話,能夠用JSON數組來存放多個JSON對象定義。在這幾種Avro定義的類型的支持下,能夠由用戶來創造出豐富的數據結構來,支持用戶紛繁複雜的數據。 

     Avro支持兩種序列化編碼方式:二進制編碼和JSON編碼。使用二進制編碼會高效序列化,而且序列化後獲得的結果會比較小;而JSON通常用於調試系統或是基於WEB的應用。對Avro數據序列化/反序列化時都須要對模式以深度優先(Depth-First),從左到右(Left-to-Right)的遍歷順序來執行。基本類型的序列化容易解決,混合類型的序列化會有不少不一樣規則。對於基本類型和混合類型的二進制編碼在文檔中規定,按照模式的解析順序依次排列字節。對於JSON編碼,聯合類型(Union Type)就與其它混合類型表現不一致。 

     Avro爲了便於MapReduce的處理定義了一種容器文件格式(Container File Format)。這樣的文件中只能有一種模式,全部須要存入這個文件的對象都須要按照這種模式以二進制編碼的形式寫入。對象在文件中以塊(Block)來組織,而且這些對象都是能夠被壓縮的。塊和塊之間會存在同步標記符(Synchronization Marker),以便MapReduce方便地切割文件用於處理。下圖是根據文檔描述畫出的文件結構圖: 
   
     上圖已經對各塊作肢解操做,但仍是有必要再詳細說明下。一個存儲文件由兩部分組成:頭信息(Header)和數據塊(Data Block)。而頭信息又由三部分構成:四個字節的前綴(相似於Magic Number),文件Meta-data信息和隨機生成的16字節同步標記符。這裏的Meta-data信息讓人有些疑惑,它除了文件的模式外,還能包含什麼。文檔中指出當前Avro認定的就兩個Meta-data:schema和codec。這裏的codec表示對後面的文件數據塊(File Data Block)採用何種壓縮方式。Avro的實現都須要支持下面兩種壓縮方式:null(不壓縮)和deflate(使用Deflate算法壓縮數據塊)。除了文檔中認定的兩種Meta-data,用戶還能夠自定義適用於本身的Meta-data。這裏用long型來表示有多少個Meta-data數據對,也是讓用戶在實際應用中能夠定義足夠的Meta-data信息。對於每對Meta-data信息,都有一個string型的key(須要以「avro.」爲前綴)和二進制編碼後的value。對於文件中頭信息以後的每一個數據塊,有這樣的結構:一個long值記錄當前塊有多少個對象,一個long值用於記錄當前塊通過壓縮後的字節數,真正的序列化對象和16字節長度的同步標記符。因爲對象能夠組織成不一樣的塊,使用時就能夠不通過反序列化而對某個數據塊進行操做。還能夠由數據塊數,對象數和同步標記符來定位損壞的塊以確保數據完整性。 
   
     上面是將Avro對象序列化到文件的操做。與之相應的,Avro也被做爲一種RPC框架來使用。客戶端但願同服務器端交互時,就須要交換雙方通訊的協議,它相似於模式,須要雙方來定義,在Avro中被稱爲消息(Message)。通訊雙方都必須保持這種協議,以便於解析從對方發送過來的數據,這也就是傳說中的握手階段。 
  
     消息從客戶端發送到服務器端須要通過傳輸層(Transport Layer),它發送消息並接收服務器端的響應。到達傳輸層的數據就是二進制數據。一般以HTTP做爲傳輸模型,數據以POST方式發送到對方去。在Avro中,它的消息被封裝成爲一組緩衝區(Buffer),相似於下圖的模型: 

       

     如上圖,每一個緩衝區以四個字節開頭,中間是多個字節的緩衝數據,最後以一個空緩衝區結尾。這種機制的好處在於,發送端在發送數據時能夠很方便地組裝不一樣數據源的數據,接收方也能夠將數據存入不一樣的存儲區。還有,當往緩衝區中寫數據時,大對象能夠獨佔一個緩衝區,而不是與其它小對象混合存放,便於接收方方便地讀取大對象。 

     下面聊下Avro的其它方面信息。前文中引述Doug Cutting的話說,Protocol Buffer在傳輸數據時,往數據中加入註釋(annotation),以應對數據結構與數據不匹配的問題。但直接致使數據量變大,解析困難等缺點。那Avro是如何應對模式與數據的不一樣呢?爲了保證Avro的高效,假定模式至少大部分是匹配的,而後定義一些驗證規則,若是在規則知足的前提下,作數據驗證。若是模式不匹配就會報錯。相同模式,交互數據時,若是數據中缺乏某個域(field),用規範中的默認值設置;若是數據中多了些與模式不匹配的數據。則忽視這些值。 

     Avro列出的優勢中還有一項是:可排序的。就是說,一種語言支持的Avro程序在序列化數據後,可由其它語言的Avro程序對未反序列化的數據排序。我不知道這種機制是在什麼樣的場景下使用,但看起來仍是挺不錯的。 

     當前關於Avro的資料挺少的,上面的文章也是我由官方文檔和做者的文章來總結的。我相信其中確定有不少錯誤,或許有些方面根本就理解錯了。如今放出這篇總結,便於不斷修訂和補充,也是對這兩天學習成果的分享,但願對想了解Avro的人有些許幫助,更但願你們指證我理解錯誤的地方,利於提升。 


算法

相關文章
相關標籤/搜索