本文根据Apache Avro官方文档所写,http://avro.apache.org/docs/current/index.html.
avro是一个数据序列化系统 提供了:
丰富的数据结构紧凑的,快速的,二进制的数据格式一种文件格式,用于存储持久化数据远程过程调用系统(RPC)和动态语言的简单交互。并不需要为数据文件读写产生代码,也不需要使用或实现RPC协议。代码生成是一种优化方式,但是只对于静态语言有意义。所谓shemas,就是一种描述文件,能够说清楚你的对象的描述文件。可能有你对象里面有什么,他们之间的逻辑关系,数据的统计信息。比如说对人的描述包括身高、体重、三维、性别等,只要他的格式确定,就可以叫做shemas。
Avro也是依赖于schemas的(为了夸语言,也为了持久化,总要有二进制数据解析的指导)。当读取Avro数据的时候需要使用到schemas, 当写数据的时候也需要有schema的指导。这样就允许了每个数据都没有前缀开销(因为可以用schema来理解数据),这就会使得序列化很快而且数据会比较小。
当把Avro数据存储到文件的时候,会把他的schema数据一起存储,这样数据就可以被接下来的任意程序使用了。如果程序希望用其他的schema来读取数据,是很简单的,因为你可以同个比较两个schema来看能不能读取数据
当Avro用于RPC的时候,客户端和服务端可以在连接握手的阶段进行schema交换。(这种方式是可以被优化的,因为对于很多schema来说,是没有schema的交换的)由于两边都有对方完整的schema存在了,所有可以比较出同名域、缺少的域、多余的域。
avro的schema是用json定义的。这样有json包的语言就会比较容易实现了。
1.下载相关JAR包
avro-1.8.1.jar (下载链接:https://mirrors.tuna.tsinghua.edu.cn/apache/avro/avro-1.8.1/java/) avro-tools-1.8.1.jar jackson-core-asl-1.9.13.jar(下载链接:) jackson-mapper-asl-1.9.13.jar 下载后将avro-1.8.1.jar and 和 Jackson jars 放到项目的类路径下 (avro-tools.jar 将被用于代码生成).
2.创建schema文件user.avsc 文件为Json格式:
{"namespace": "example.avro", "type": "record", "name": "User", "fields": [ {"name": "name", "type": "string"}, {"name": "favorite_number", "type": ["int", "null"]}, {"name": "favorite_color", "type": ["string", "null"]} ] }3.使用代码生成(code generation)进行序列化/反序列化
编译schema,生成java代码包 java -jar /path/to/avro-tools-1.8.1.jar compile schema < schema file> < destination>
可以看到生成了一个代码包,里面有一个User.java文件,这个文件就是根据Schemas生成的,将代码包直接导入项目src下。注意:项目的包名称应与Schemas文件的namespace名称一致。
序列化与反序列化代码如下:
/** * Serializing and deserializing with code generation */ public class AvroWithCode { public static void main(String[] args) throws Exception { AvroWithCode ta=new AvroWithCode(); ta.deserializing(); } /** * 串行化 */ public void serializing() throws Exception{ User user1 = new User(); user1.setName("tom"); user1.setFavoriteColor("red"); user1.setFavoriteNumber(6); User user2 = new User("Ben", 7, "red"); User user3 = User.newBuilder().setName("Charlie") .setFavoriteColor("blue").setFavoriteNumber(null).build(); // 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("G:/avro/users.avro")); dataFileWriter.append(user1); dataFileWriter.append(user2); dataFileWriter.append(user3); dataFileWriter.close(); System.out.println("Serialized over"); } /** * 反串行化 * @throws Exception */ public void deserializing() throws Exception{ // Deserialize Users from disk DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class); DataFileReader<User> dataFileReader = new DataFileReader<User>(new File("G:/avro/users.avro"), userDatumReader); User user = null; while (dataFileReader.hasNext()) { user = dataFileReader.next(user); System.out.println(user); } } }4.也可以不使用代码生成(code generation)进行序列化/反序列化 相关代码:
/** * Serializing and deserializing without code generation */ public class AvroWithoutCode { public static void main(String[] args) throws Exception { AvroWithoutCode ta = new AvroWithoutCode(); ta.serializing(); ta.deserializing(); } /** * 串行化 */ public void serializing() throws Exception { Schema schema = new Schema.Parser().parse(new File("G:/avro/users.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"); // Serialize user1 and user2 to disk File file = new File("G:/avro/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(); System.out.println("serialized over"); } /** * 反串行化 * * @throws Exception */ public void deserializing() throws Exception { // Deserialize users from disk Schema schema = new Schema.Parser().parse(new File("G:/avro/users.avsc")); DatumReader<GenericRecord> datumReader = new GenericDatumReader<GenericRecord>(schema); DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(new File("G:/avro/users.avro"), datumReader); GenericRecord user = null; while (dataFileReader.hasNext()) { user = dataFileReader.next(user); System.out.println(user); } } }以上便是Avro进行串行/反串行化的方法。
avro提供了功能上和Thrift、Protocol Buffers等相同的特性。但也有自己的不同点:
动态类型:Avro不需要生成代码,数据总是和schema放在一起的,使得不用代码生成或静态类型就可以做所有必要的处理这种特性使得使用简单语言构造一般的通用系统变得很方便没有加标签的数据:因为在读取数据的时候,是有schema在的,所以很少的类型的信息需要加载进入数据中,并且序列化数据更小没有手动的序列化ID:当一个schema变化后,新旧schema在处理数据时就会都有,这样两者之间的不同就可以直接通过符号来处理,也就是属性名称