文章目录
  1. 1. 特点
  2. 2. 横向对比
    1. 2.1. thrift和avro对比
    2. 2.2. Schema处理
    3. 2.3. 序列化的方式
    4. 2.4. RPC的服务
    5. 2.5. 总结
    6. 2.6. 官网给出的对比
    7. 2.7. 为何不使用java序列化
  3. 3. 实例
    1. 3.1. 代码生成
    2. 3.2. 直接使用schema
  4. 4. 参考

Avro是Hadoop中的一个子项目,也是Apache中一个独立的项目,Avro是一个基于二进制数据传输高性能的中间件。在Hadoop的其他项目中例如HBase(Ref)和Hive(Ref)的Client端与服务端的数据传输也采用了这个工具。Avro是一个数据序列化的系统。Avro 可以将数据结构或对象转化成便于存储或传输的格式。Avro设计之初就用来支持数据密集型应用,适合于远程或本地大规模数据的存储和交换。

特点

  1. 丰富的数据结构类型;
  2. 快速可压缩的二进制数据形式,对数据二进制序列化后可以节约数据存储空间和网络传输带宽;
  3. 存储持久数据的文件容器;
  4. 可以实现远程过程调用RPC;
  5. 简单的动态语言结合功能,Avro和动态语言结合后,读写数据文件和使用RPC协议都不需要生成代码,而代码生成作为一种可选的优化只值得在静态类型语言中实现。

横向对比

同类软件还有:

  1. thrift:由 Facebook 主导开发的 , 一个跨平台,支持多语言的,通过定义 IDL 文件,自动生成 RPC 客户端与服务端通信代码的工具,以构建在多种编程语言间无缝结合的、高效的服务
  2. protocol buffer:一种序列化与结构化数据的一种机制,具有跨平台、解析速度快、序列化数据体积小、扩展性高、使用简单的特点

thrift和avro对比

Schema处理

  1. thrift依赖IDL–>代码的生成,静态的。走代码生成,编译载入的流程。
  2. Avro可以生成代码,后编译执行,但是还必须依赖IDL(meta元数据描述);也可以走动态解释执行IDL序列化的方式

序列化的方式

  1. thrift

    提供多种序列化实现,TCompactProtocol,TBinaryProtocol

    每个Field前面都是带Tag的,这个Tag用于标识这个域的类型和顺序ID(IDL中定义,用于Versioning)。在同一批数据里面,这些Tag的信息是完全相同的,当数据条数大的时候这显然就浪费了。

  2. Acro

    格式包括:文件头中有schema+数据records(自描述)

    只对感兴趣的部分反序列化

    schema允许定义数据的排序order

    采用block链表结构,突破了用单一整型表示大小的限制。比如Array或Map由一系列Block组成,每个Block包含计数器和对应的元 素,计数器为0标识结束。

RPC的服务

  1. Thrift

    TThreadPolServer: 多线程服务

    TNonBlockingServer: 单线程 non blocking的服务

    THsHaServer: 多线程 non blocking的服务

  2. Avro

    HttpServer : 缺省,基于Jetty内核的服务.

    NettyServer: 新的基于Netty的服务.

总结

  1. Thrift适用于程序对程序静态的数据交换,要求schema预知并相对固定
  2. Avro在Thrift基础上增加了对schema动态的支持且性能上不输于Thrift
  3. Avro显式schema设计使它更适用于搭建数据交换及存储的通用工具和平台,特别是在后台
  4. 目前Thrift的优势在于更多的语言支持和相对成熟
  5. PB具有跨平台、解析速度快、序列化数据体积小、扩展性高、使用简单的特点,但是内嵌并没有提供RPC的通讯

Avro的创新之处在于融合了显式,declarative的Schema和高效二进制的数据表达,强调数据的自我描述,克服了以往单纯XML或二进制系统的缺陷。Avro对Schema动态加载功能,是Thrift编程接口所不具备的,符合了Hadoop上的Hive/Pig及NOSQL 等既属于ad hoc,又追求性能的应用需求.

官网给出的对比

  1. 动态类型:Avro并不需要生成代码,模式和数据存放在一起,而模式使得整个数据的处理过程并不生成代码、静态数据类型等等。这方便了数据处理系统和语言的构造。
  2. 未标记的数据:由于读取数据的时候模式是已知的,那么需要和数据一起编码的类型信息就很少了,这样序列化的规模也就小了。
  3. 不需要用户指定字段号:即使模式改变,处理数据时新旧模式都是已知的,所以通过使用字段名称可以解决差异问题。

为何不使用java序列化

  1. java序列化不够灵活,不能更好的控制序列化的整个流程
  2. java序列化不符合序列化的标准,没有做一定的压缩,java序列化首先写类名,然后再是整个类的数据,而且成员对象在序列化中只存引用,成员对象的可以出现的位置很随机,既可以在序列化的对象前,也可以在其后面,这样就对随机访问造成影响,一旦出错,整个后面的序列化就会全部错误
  3. Java序列化每次序列化都要重新创建对象,内存消耗大。

实例

因为目前我们使用Avro主要是用来进行序列化,所以下面给出的实例也是对Avro序列化功能的展示。

首先定义一个schema: user.avsc:

{"namespace": "example.avro",
 "type": "record",
 "name": "User",
 "fields": [
     {"name": "name", "type": "string"},
     {"name": "favorite_number",  "type": ["int", "null"]},
     {"name": "favorite_color", "type": ["string", "null"]}
 ]
}

使用Avro进行序列化和反序列化有两种不同的途径:代码生成和不走代码生成。

代码生成

首先使用schema生成Java类: User.class。

  1. 创建User

    User user1 = new User();
    user1.setName("Alyssa");
    user1.setFavoriteNumber(256);
    // Leave favorite color null
    
    // Alternate constructor
    User user2 = new User("Ben", 7, "red");
    
    // Construct via builder
    User user3 = User.newBuilder()
                 .setName("Charlie")
                 .setFavoriteColor("blue")
                 .setFavoriteNumber(null)
                 .build();
    
  2. 序列化

    // Serialize user1, user2 and user3 to disk
    DatumWriter<User> userDatumWriter = new SpecificDatumWriter<User>(User.class);
    DataFileWriter<User> dataFileWriter = new DataFileWriter<User>(userDatumWriter);
    dataFileWriter.create(user1.getSchema(), new File("users.avro"));
    dataFileWriter.append(user1);
    dataFileWriter.append(user2);
    dataFileWriter.append(user3);
    dataFileWriter.close();
    
  3. 反序列化

    // Deserialize Users from disk
    DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class);
    DataFileReader<User> dataFileReader = new DataFileReader<User>(file, userDatumReader);
    User user = null;
    while (dataFileReader.hasNext()) {
    // Reuse user object by passing it to next(). This saves us from
    // allocating and garbage collecting many objects for files with
    // many items.
    user = dataFileReader.next(user);
    System.out.println(user);
    }
    

直接使用schema

  1. 创建User

    Schema schema = new Schema.Parser().parse(new File("user.avsc"));
    GenericRecord user1 = new GenericData.Record(schema);
    user1.put("name", "Alyssa");
    user1.put("favorite_number", 256);
    // Leave favorite color null
    
    GenericRecord user2 = new GenericData.Record(schema);
    user2.put("name", "Ben");
    user2.put("favorite_number", 7);
    user2.put("favorite_color", "red");
    
  2. 序列化

    // Serialize user1 and user2 to disk
    File file = new File("users.avro");
    DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(schema);
    DataFileWriter<GenericRecord> dataFileWriter = new DataFileWriter<GenericRecord>(datumWriter);
    dataFileWriter.create(schema, file);
    dataFileWriter.append(user1);
    dataFileWriter.append(user2);
    dataFileWriter.close();
    
  3. 反序列化

    // Deserialize users from disk
    DatumReader<GenericRecord> datumReader = new GenericDatumReader<GenericRecord>(schema);
    DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(file, datumReader);
    GenericRecord user = null;
    while (dataFileReader.hasNext()) {
    // Reuse user object by passing it to next(). This saves us from
    // allocating and garbage collecting many objects for files with
    // many items.
    user = dataFileReader.next(user);
    System.out.println(user);
    

参考

  1. Apache Avro™ 1.7.7 Documentation
文章目录
  1. 1. 特点
  2. 2. 横向对比
    1. 2.1. thrift和avro对比
    2. 2.2. Schema处理
    3. 2.3. 序列化的方式
    4. 2.4. RPC的服务
    5. 2.5. 总结
    6. 2.6. 官网给出的对比
    7. 2.7. 为何不使用java序列化
  3. 3. 实例
    1. 3.1. 代码生成
    2. 3.2. 直接使用schema
  4. 4. 参考