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的人有些許幫助,更希望大家指證我理解錯誤的地方,利於提高。
 




   其它資料: 
   Avro規範:http://avro.apache.org/docs/current/spec.html 
   Doug Cutting文章:http://www.cloudera.com/blog/2009/11/avro-a-new-format-for-data-interchange/ 

   各序列化系統性能比較:http://wiki.github.com/eishay/jvm-serializers/

http://langyu.iteye.com/blog/708568

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