当前位置: 首页 > 工具软件 > Apache Avro > 使用案例 >

Apache Avro 的基本功能

薛弘壮
2023-12-01

介绍

Apache Avro™是一个数据序列化系统。

Avro提供:

  • 丰富的数据结构。
  • 紧凑,快速的二进制数据格式。
  • 一个容器文件,用于存储持久性数据。
  • 远程过程调用(RPC)。
  • 与动态语言的简单集成。读取或写入数据文件不需要代码生成,也不需要使用或实现RPC协议。代码生成是一种可选的优化,只有静态类型语言才值得实现。

Schemas

所谓shemas,就是一种描述文件,能够说清楚你的对象的描述文件。可能有你对象里面有什么,他们之间的逻辑关系,数据的统计信息。比如说对人的描述包括身高、体重、三维、性别等,只要他的格式确定,就可以叫做shemas。

Avro也是依赖于schemas的(为了夸语言,也为了持久化,总要有二进制数据解析的指导)。当读取Avro数据的时候需要使用到schemas, 当写数据的时候也需要有schema的指导。这样就允许了每个数据都没有前缀开销(因为可以用schema来理解数据),这就会使得序列化很快而且数据会比较小。

当把Avro数据存储到文件的时候,会把他的schema数据一起存储,这样数据就可以被接下来的任意程序使用了。如果程序希望用其他的schema来读取数据,是很简单的,因为你可以同个比较两个schema来看能不能读取数据

当Avro用于RPC的时候,客户端和服务端可以在连接握手的阶段进行schema交换。(这种方式是可以被优化的,因为对于很多schema来说,是没有schema的交换的)由于两边都有对方完整的schema存在了,所有可以比较出同名域、缺少的域、多余的域。

avro的schema是用json定义的。这样有json包的语言就会比较容易实现了。

与其他系统比较

Avro提供的功能类似于Thrift, Protocol Buffers等系统。Avro在以下基本方面与这些系统不同。

  • 动态输入:Avro不需要生成代码。数据总是伴随着一个模式,允许在不生成代码的情况下完全处理该数据,静态数据类型等。这有助于构建通用数据处理系统和语言。
  • 未标记的数据:由于在读取数据时存在模式,因此需要使用数据编码少得多的类型信息,从而导致更小的序列化大小。
  • 没有手动分配的字段ID:当模式更改时,旧数据和新模式在处理数据时总是存在,因此可以使用字段名称以符号方式解析差异。
  • 语言无关的数据序列化和RPC框架,用于解决Hadoop中Writable序列化机制的缺点:缺少跨语言特性,与Java绑的太紧,数据格式很难被JVM外的语言进行处理。

基本功能

1.序列化和反序列化

Serializing and deserializing with code generation

Download

<dependency>
  <groupId>org.apache.avro</groupId>
  <artifactId>avro</artifactId>
  <version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro-tools</artifactId>
<version>1.8.2</version>
</dependency>

Defining a schema


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

Compiling the schema


java -jar /path/to/avro-tools-1.8.2.jar compile schema user.avsc .

Creating Users


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();

Serializing

// 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();

Deserializing

// 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);
}

This snippet will output:

{"name": "Alyssa", "favorite_number": 256, "favorite_color": null}
{"name": "Ben", "favorite_number": 7, "favorite_color": "red"}
{"name": "Charlie", "favorite_number": null, "favorite_color": "blue"}

Serializing and deserializing without code generation


Creating users

First, we use a Parser to read our schema definition and create a Schema object.

Schema schema = new Schema.Parser().parse(new File("user.avsc"));

Using this schema, let's create some users.

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");

Serializing

// 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();

Deserializing

Finally, we'll deserialize the data file we just created.

// 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);

This outputs:

{"name": "Alyssa", "favorite_number": 256, "favorite_color": null}
{"name": "Ben", "favorite_number": 7, "favorite_color": "red"}


2.RPC

 类似资料: