1. 简介
Apache Avro(以下简称 Avro)是一种与编程语言无关的序列化格式。Doug Cutting 创建了这个项目,目的是提供一种共享数据文件的方式。
Avro 数据通过与语言无关的 schema 来定义。schema 通过 JSON 来描述,数据被序列化成二进制文件或 JSON 文件,不过一般会使用二进制文件。Avro 在读写文件时需要用到 schema,schema 一般会被内嵌在数据文件里。
Avro 有一个很有意思的特性是,当负责写消息的应用程序使用了新的 schema,负责读消息的应用程序可以继续处理消息而无需做任何改动。
到写本篇博客的时间为止,avro的最新版本为1.8.2
2. 创建 maven 工程
(1) 加入 avro 依赖
org.apache.avro
avro
1.8.2
(2) 加入 avro 插件的依赖
org.apache.avro
avro-maven-plugin
1.8.2
generate-sources
schema
${project.basedir}/src/main/avro/
${project.basedir}/src/main/java/
org.apache.maven.plugins
maven-compiler-plugin
1.6
1.6
以上是官网列出的 avro 插件的依赖,其中提供了 maven 的编译插件,该插件使用JDK1.6版本来编译代码,我在这里改为了1.8,因为我的JDK版本是1.8
org.apache.maven.plugins
maven-compiler-plugin
1.8
1.8
在引入这个插件后,在 pom.xml 中会有编译错误(错误原因我也不清楚),选择忽略即可
选择忽略之后,在 pom 中会自动生成以下配置来说明 pom 文件已经忽略了 avro 插件引起的错误
org.eclipse.m2e
lifecycle-mapping
1.0.0
org.apache.avro
avro-maven-plugin
[1.8.2,)
schema
以上错误是在 Eclipse 中创建 avro 的 maven 项目时才会出现,在 IDEA 中就不会出现这种情况。
(3) 更新 maven 工程
作了以上修改后,发现 maven 项目上有报错,但 pom 中并没有错误:
在项目上右键更新maven项目即可:
3. 使用 avro
(1) 通过生成代码的方式使用 avro
<1> 定义 schema 文件
注意在 avro 插件的依赖中定义的两个路径
${project.basedir}/src/main/avro/
${project.basedir}/src/main/java/
该配置的意思是,根据/src/main/avro/下的schema文件,生成对应的类文件到/src/main/java/下,所以我们先创建一个资源文件夹/src/main/avro
然后再在该资源文件夹下创建 schema 文件,这里定义一个简单的schema文件user.avsc
,注意,后缀一定是avsc
,其中的内容如下:
{
"namespace": "com.avro.example",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]}
]
}
- namespace:定义了根据 schema 文件生成的类的包名
- type:固定写法
- name:生成的类的名称
- fields:定义了生成的类中的属性的名称和类型,其中
"type": ["int", "null"]
的意思是,favorite_number 这个属性是int类型,但可以为null
avro 支持的类型有null、boolean、int、long、float、double、bytes、string这些基本类型和record、enum、array、map、union、fixed这些复杂类型,关于复杂类型可以参考官网的说明:http://avro.apache.org/docs/current/spec.html#schema_complex,本文只是一个入门
<2> 生成 User 类
在编译程序之前,项目中是没有com.avro.example.User这个类的:
在运行 maven build compile 后,就生成这个类:
<3> 序列化
package com.avro.serializer;
import java.io.File;
import java.io.IOException;
import org.apache.avro.file.DataFileWriter;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.specific.SpecificDatumWriter;
import com.avro.example.User;
/**
* @Title AvroSerializerTest.java
* @Description 使用 avro 对 com.avro.example.User 类的对象进行序列化
* @Author YangYunhe
* @Date 2018-06-21 15:42:02
*/
public class AvroSerializerTest {
public static void main(String[] args) throws IOException {
User user1 = new User();
user1.setName("Tom");
user1.setFavoriteNumber(7);
User user2 = new User("Jack", 15, "red");
User user3 = User.newBuilder()
.setName("Harry")
.setFavoriteNumber(1)
.setFavoriteColor("green")
.build();
DatumWriter userDatumWriter = new SpecificDatumWriter<>(User.class);
DataFileWriter dataFileWriter = new DataFileWriter(userDatumWriter);
dataFileWriter.create(user1.getSchema(), new File("users.avro"));
dataFileWriter.append(user1);
dataFileWriter.append(user2);
dataFileWriter.append(user3);
dataFileWriter.close();
}
}
运行以上程序,就会把这3个User对象经过 avro 序列化后写到了项目根目录下的"user.avro"文件中:
<4> 反序列化
package com.avro.deserializer;
import java.io.File;
import java.io.IOException;
import org.apache.avro.file.DataFileReader;
import org.apache.avro.io.DatumReader;
import org.apache.avro.specific.SpecificDatumReader;
import com.avro.example.User;
/**
* @Title AvroDeSerializerTest.java
* @Description 解析 avro 序列化后的对象
* @Author YangYunhe
* @Date 2018-06-21 15:58:10
*/
public class AvroDeSerializerTest {
public static void main(String[] args) throws IOException {
DatumReader userDatumReader = new SpecificDatumReader(User.class);
DataFileReader dataFileReader = new DataFileReader(new File("users.avro"), userDatumReader);
User user = null;
while (dataFileReader.hasNext()) {
user = dataFileReader.next(user);
System.out.println(user);
}
}
}
程序运行结果为:
{"name": "Tom", "favorite_number": 7, "favorite_color": null}
{"name": "Jack", "favorite_number": 15, "favorite_color": "red"}
{"name": "Harry", "favorite_number": 1, "favorite_color": "green"}
(2) 通过不生成代码的方式使用 avro
<1> 序列化
package com.avro.serializer;
import java.io.File;
import java.io.IOException;
import org.apache.avro.Schema;
import org.apache.avro.file.DataFileWriter;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericDatumWriter;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.DatumWriter;
import com.avro.deserializer.AvroDeSerializerWithoutCodeGenerationTest;
/**
* @Title AvroSerializerWithoutCodeGenerationTest.java
* @Description 通过不生成代码的方式使用avro序列化User对象
* @Author YangYunhe
* @Date 2018-06-21 16:04:13
*/
public class AvroSerializerWithoutCodeGenerationTest {
public static void main(String[] args) throws IOException {
String avscFilePath =
AvroDeSerializerWithoutCodeGenerationTest.class.getClassLoader().getResource("user.avsc").getPath();
Schema schema = new Schema.Parser().parse(new File(avscFilePath));
GenericRecord user1 = new GenericData.Record(schema);
user1.put("name", "Tony");
user1.put("favorite_number", 18);
GenericRecord user2 = new GenericData.Record(schema);
user2.put("name", "Ben");
user2.put("favorite_number", 3);
user2.put("favorite_color", "red");
File file = new File("user2.avro");
DatumWriter datumWriter = new GenericDatumWriter(schema);
DataFileWriter dataFileWriter = new DataFileWriter(datumWriter);
dataFileWriter.create(schema, file);
dataFileWriter.append(user1);
dataFileWriter.append(user2);
dataFileWriter.close();
}
}
<2> 反序列化
package com.avro.deserializer;
import java.io.File;
import java.io.IOException;
import org.apache.avro.Schema;
import org.apache.avro.file.DataFileReader;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.DatumReader;
/**
* @Title AvroDeSerializerWithoutCodeGenerationTest.java
* @Description 通过不生成代码的方式使用avro反序列化
* @Author YangYunhe
* @Date 2018-06-21 16:07:44
*/
public class AvroDeSerializerWithoutCodeGenerationTest {
public static void main(String[] args) throws IOException {
String avscFilePath =
AvroDeSerializerWithoutCodeGenerationTest.class.getClassLoader().getResource("user.avsc").getPath();
Schema schema = new Schema.Parser().parse(new File(avscFilePath));
File file = new File("user2.avro");
DatumReader datumReader = new GenericDatumReader(schema);
DataFileReader dataFileReader = new DataFileReader(file, datumReader);
GenericRecord user = null;
while (dataFileReader.hasNext()) {
user = dataFileReader.next(user);
System.out.println(user);
}
}
}
程序运行结果:
{"name": "Tony", "favorite_number": 18, "favorite_color": null}
{"name": "Ben", "favorite_number": 3, "favorite_color": "red"}