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的人有些许帮助,更但愿你们指证我理解错误的地方,利于提升。 


算法

相关文章
相关标签/搜索