Apache Avro 入门

1. 简介

Apache Avro(以下简称 Avro)是一种与编程语言无关的序列化格式。Doug Cutting 创建了这个项目,目的是提供一种共享数据文件的方式。

Avro 数据通过与语言无关的 schema 来定义。schema 通过 JSON 来描述,数据被序列化成二进制文件或 JSON 文件,不过一般会使用二进制文件。Avro 在读写文件时需要用到 schema,schema 一般会被内嵌在数据文件里。

Avro 有一个很有意思的特性是,当负责写消息的应用程序使用了新的 schema,负责读消息的应用程序可以继续处理消息而无需做任何改动。

到写本篇博客的时间为止,avro的最新版本为1.8.2

2. 创建 maven 工程

(1) 加入 avro 依赖

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

(2) 加入 avro 插件的依赖

<plugin>
  <groupId>org.apache.avro</groupId>
  <artifactId>avro-maven-plugin</artifactId>
  <version>1.8.2</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>

以上是官网列出的 avro 插件的依赖,其中提供了 maven 的编译插件,该插件使用JDK1.6版本来编译代码,我在这里改为了1.8,因为我的JDK版本是1.8

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <source>1.8</source>
    <target>1.8</target>
  </configuration>
</plugin>

在引入这个插件后,在 pom.xml 中会有编译错误(错误原因我也不清楚),选择忽略即可

选择忽略之后,在 pom 中会自动生成以下配置来说明 pom 文件已经忽略了 avro 插件引起的错误

<pluginManagement>
    <plugins>
        <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
        <plugin>
            <groupId>org.eclipse.m2e</groupId>
            <artifactId>lifecycle-mapping</artifactId>
            <version>1.0.0</version>
            <configuration>
                <lifecycleMappingMetadata>
                    <pluginExecutions>
                        <pluginExecution>
                            <pluginExecutionFilter>
                                <groupId>org.apache.avro</groupId>
                                <artifactId>avro-maven-plugin</artifactId>
                                <versionRange>[1.8.2,)</versionRange>
                                <goals>
                                    <goal>schema</goal>
                                </goals>
                            </pluginExecutionFilter>
                            <action>
                                <ignore></ignore>
                            </action>
                        </pluginExecution>
                    </pluginExecutions>
                </lifecycleMappingMetadata>
            </configuration>
        </plugin>
    </plugins>
</pluginManagement>

以上错误是在 Eclipse 中创建 avro 的 maven 项目时才会出现,在 IDEA 中就不会出现这种情况。

(3) 更新 maven 工程

作了以上修改后,发现 maven 项目上有报错,但 pom 中并没有错误:

在项目上右键更新maven项目即可:

3. 使用 avro

(1) 通过生成代码的方式使用 avro

<1> 定义 schema 文件

注意在 avro 插件的依赖中定义的两个路径

<configuration>
    <sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>
    <outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
</configuration>

该配置的意思是,根据/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<User> userDatumWriter = new SpecificDatumWriter<>(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个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<User> userDatumReader = new SpecificDatumReader<User>(User.class);
        DataFileReader<User> dataFileReader = new DataFileReader<User>(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<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();
    }
}

<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<GenericRecord> datumReader = new GenericDatumReader<GenericRecord>(schema);
        DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(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"}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android群英传

Retrofit源码分析

14440
来自专栏逢魔安全实验室

铁人三项2018 pwn [heapmain] Writeup

这个题目的原题是RHME3,直接拿来二进制修改,去掉网络函数,使用socat部署。这波操作可还行

11920
来自专栏Java 源码分析

synchronized 原理分析

synchronized 原理分析 1. 在阅读源码时做了大量的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限,并且代码阅读起来没有 IDE 方...

26930
来自专栏数据结构笔记

python编写简单聊天程序

18420
来自专栏JavaEdge

JMM1、基础与概念2、重排序6、锁7、java concurrent包的通用化的实现模式7、final8、双重检查和延迟优化

32190
来自专栏技术墨客

Hazelcast集群服务(4)——分布式Map

    在第一篇介绍Hazelcast的文章已经提到,Hazelcast为Java中绝大部分数据结构提供了分布式实现。我们常用的Map、List、Queue等数...

35130
来自专栏高性能服务器开发

详解C/C++中volatile关键字

volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volat...

15730
来自专栏大闲人柴毛毛

深入剖析Spring(一)——IoC的基本概念(从面向对象角度介绍)

IoC与DI IoC和DI是Spring的两个核心概念,很多人都把它们视为相同的东西,但事实并非如此。 IoC(Inversion of Control)...

36550
来自专栏落影的专栏

静态库与动态库的思考

前言 在上文《编译与链接过程的思考》评论中暴走大牙提到了静态库和动态库依赖的问题,还在群里提了几个测试样例和测试工程。 大致介绍下测试工程和如何进行测试: ...

39760
来自专栏zcqshine's blog

PHP函数uasort()在类中的使用问题解决

36590

扫码关注云+社区

领取腾讯云代金券