Avro - 简单介绍,Java版 - 序列化与反序列化官方例子
Avro - 简单介绍,Java版 - 序列化与反序列化官方例子英文原文http://avro.apache.org/docs/1.7.7/gettingstartedjava.htmlHenvealfAvro 简单介绍数据序列化系统。丰富的数据结构类型;快速可压缩的二进制数据形式,对数据二进制序列化后可以节约数据存储空间和网络传输带宽;存储持久数据的文件容器;可以实现远程过程调用RPC;
Avro - 简单介绍,Java版 - 序列化与反序列化官方例子
英文原文 http://avro.apache.org/docs/1.7.7/gettingstartedjava.html
Henvealf
Avro 简单介绍
数据序列化系统。
- 丰富的数据结构类型;
- 快速可压缩的二进制数据形式,对数据二进制序列化后可以节约数据存储空间和网络传输带宽;
- 存储持久数据的文件容器;
- 可以实现远程过程调用RPC;
- 简单的动态语言结合功能。
模式
Avro 依赖于 schema. 当 Avro 数据读取时, 就是用当前写入时的模式。结构和数据值放在一块,能让序列化变得又快有小。当数据和模式放在一起的时候,有利于在动态脚本语言上使用。
当 Avro 数据存储在文件中,模式也一块存在里面。所以文件之后可能会被任何的程序处理。如果程序读取的数据和期望的模式不同,Avro 也能很好的处理。
在使用 RPC 时,客户端于服务端在连接握手时交换模式,这样客户端于服务端就都有了对方的全部模式,这样他们就能简单的处理两个模式之间的差异。
Avro 使用 json 定义模式。
maven 连接
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro</artifactId>
<version>1.7.7</version>
</dependency>
Avro 的 Maven 插件,用于执行代码生成
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.7.7</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>
${project.basedir}/src/main/avro/
</sourceDirectory>
<outputDirectory>
${project.basedir}/src/main/java/
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
定义一个 schema
- 使用 Json 来定义。
- 分为基本类型(null, boolean, int, long, float, double, bytes, and string)和 复杂类型(record, enum, array, map, union, and fixed)
下面是一个简单的模式
{"namespace": "example.avro",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]}
]
}
- 上面的模式定义了一个假象的用户,
- 一个文件只能包含一个单独的模式定义
- 一个 record 定义至少包括他的类型(”type”: “record”),一个名字(”name”: “User”),还有 fields 。
- 然后 namespace,命名空间,使用上栗,schema 的完整名字就是 example.avro.User
- fields 中的内容使用一个数组来定义,数组中的元素中就是 name 与 type,当然有其他选项可选。type 属性的值是其他属性对象。可以是复杂或者基本类型。
- name 就为基本属性,favorite_* 就为复杂属性,意思其类型要么是 int/string,要么就是null。
生成代码来序列化与反序列化。
编译模式
可以使用代码,根据上面的模式自动创建出类。直接使用 avro 工具 jar 便可以:
java -jar /path/to/avro-tools-1.7.7.jar compile schema <schema file> <destination>
java -jar /path/to/avro-tools-1.7.7.jar compile schema user.avsc .
下面我们就能在当前目录看到使用命名空间定义的目录 ./example/avro ,目录最深处就会有一个 User.class
如果你使用了 Avro Maven plugin,就会自动执行代码生成。
创建 Users
现在,就可以在代码中创建对应的 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();
三种创建方式。
builders 会在创建时候自动设置在 schema 中的默认值,builders会在被设置的时候检查,而使用对象构造器,如果字段写错,序列化的时候造成一个error。
然而,使用构造器直接生成会提供更高的性能,而 builder 会在写入钱创建一个数据结构的拷贝。
序列化
// 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();
我们创建一个 DatumWriter,将 Java 对象转化为内存中的序列格式。SpecificDatumWriter 使用 之前生成的类并且从中提取出指定的模式。
下一步,我们创建了一个 DataFileWriter,将已经序列化的数据,写入调用 dataFileWriter.create 时指定的文件中。最后使用 dataFileWriter.append 将我们的user对象写入到序列化文件中。
反序列化
// Deserialize Users from disk
DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class);
DataFileReader<User> dataFileReader = new DataFileReader<User>(file, userDatumReader);
// 使用这种方式可以避免因为对象太多而造成的对 gc 的不良影响。
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);
}
输出结果为:
{"name": "Alyssa", "favorite_number": 256, "favorite_color": null}
{"name": "Ben", "favorite_number": 7, "favorite_color": "red"}
{"name": "Charlie", "favorite_number": null, "favorite_color": "blue"}
编译并运行示例代码
$ mvn compile # includes code generation via Avro Maven plugin
$ mvn -q exec:java -Dexec.mainClass=example.SpecificMain
不使用代码生成来序列以及反序列化。
Avro 中的 schema 和数据经常放在一起。
创建 Users
首先,我们使用一个 Parser 读取我们的 schema 定义并创建一个 Schema 对象。
Schema schema = new Schema.Parser().parse(new File("user.avsc"));
然后使用该 schema ,我们来创建一些 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");
GenericRecords 就代表 users。 GenericRecords 使用 schema 去验证我们指定的字段,如果我们指定的字段在 schema 中不存在,就会抛出 AvroRuntimeException 。
序列化
我们的序列化与序列化过程与上面使用代码生成时的过程几乎一样,不一样的是 reader 和 writer 所使用的类。
// 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();
发现这里用的是 GenericDatumWriter。不多说了。
反序列化
// 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);
}
输出为:
{"name": "Alyssa", "favorite_number": 256, "favorite_color": null}
{"name": "Ben", "favorite_number": 7, "favorite_color": "red"}
可以发现在这里也就仅仅换了一个类 GenericDatumReader.
编译并运行代码
$ mvn compile
$ mvn -q exec:java -Dexec.mainClass=example.GenericMain
End!!
更多推荐
所有评论(0)