阿里云「飞天发布时刻」2024来啦!新产品、新特性、新能力、新方案,等你来探~ 了解详情
写点什么

使用 Apache Avro

  • 2011-03-11
  • 本文字数:12482 字

    阅读完需:约 41 分钟

Avro [1] 是最近加入到 Apache 的 Hadoop 家族的项目之一。为支持数据密集型应用,它定义了一种数据格式并在多种编程语言中支持这种格式。

Avro 提供的功能类似于其他编组系统,如 Thrift、Protocol Buffers 等。而 Avro 的主要不同之处在于 [2] :

  • “动态类型:Avro 无需生成代码。数据总是伴以模式定义,这样就可以在不生成代码、静态数据类型的情况下对数据进行所有处理。这样有利于构建通用的数据处理系统和语言。
  • 无标记数据: 由于在读取数据时有模式定义,这就大大减少了数据编辑所需的类型信息,从而减少序列化空间。
  • 不用手动分配的字段 ID: 当数据模式发生变化,处理数据时总是同时提供新旧模式,差异就可以用字段名来做符号化的分析。”

由于性能高、基本代码少和产出数据量精简等特点,Avro 周围展开了众多活动——许多 NoSQL 实现,包括 Hadoop、Cssandra 等,都把 Avro 整合到它们的客户端 API 和储存功能中;已经有人对 Avro 与其他流行序列化框架做了 Benchmark 测试并得到结果 [3] ,但是,目前尚无可供人们学习使用 Avro 的代码示例 [4]

在这篇文章中我将试着描述我使用 Avro 的经验,特别是:

  • 如何建立组件化 Avro 模式,使用组件搭建整体模式,分别保存在多个文件中
  • 在 Avro 中实现继承
  • 在 Avro 中实现多态
  • Avro 文档的向后兼容性。

组件化 Apache Avro 模式

如 Avro 规范所述 [5] Avro 文档模式定义成 JSON 文件。在当前 Avro 实现中,模式类需要一个文件(或字符串)来表示内部模式。同 XML 模式不一样,Avro 当前版本不支持向模式文档中导入(一个或多个)子模式,这往往迫使开发者编写非常复杂的模式定义 [6] ,并大大复杂化了模式的重用。下面的代码示例给出了一个有趣的拆分和组合模式文件的例子。它基于模式类提供的一个 toString()方法,该方法返回一个 JSON 字符串以表示给定的模式定义。用这种办法,我提供了一个简单 AvroUtils,能够自动完成上述功能:

复制代码
package com.navteq.avro.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.avro.Schema;
public class AvroUtils {
private static Map<String, Schema> schemas = new HashMap<String, Schema>();
private AvroUtils(){}
public static void addSchema(String name, Schema schema){
schemas.put(name, schema);
}
public static Schema getSchema(String name){
return schemas.get(name);
}
public static String resolveSchema(String sc){
String result = sc;
for(Map.Entry<String, Schema> entry : schemas.entrySet())
result = replace(result, entry.getKey(),
entry.getValue().toString());
return result;
}
static String replace(String str, String pattern, String replace) {
int s = 0;
int e = 0;
StringBuffer result = new StringBuffer();
while ((e = str.indexOf(pattern, s)) >= 0) {
result.append(str.substring(s, e));
result.append(replace);
s = e+pattern.length();
}
result.append(str.substring(s));
return result.toString();
}
public static Schema parseSchema(String schemaString){
String completeSchema = resolveSchema(schemaString);
Schema schema = Schema.parse(completeSchema);
String name = schema.getFullName();
schemas.put(name, schema);
return schema;
}
public static Schema parseSchema(InputStream in)throws IOException {
StringBuffer out = new StringBuffer();
byte[] b = new byte[4096];
for (int n; (n = in.read(b)) != -1;) {
out.append(new String(b, 0, n));
}
return parseSchema(out.toString());
}
public static Schema parseSchema(File file)throws IOException {
FileInputStream fis = new FileInputStream(file);
return parseSchema(fis);
}
}

清单 1 AvroUtils 类

这个简单实现基于全局(静态)模式注册表,它由完全限定的模式名及与之对应的对象构成。对于每一个要解析的新模式,该实现在注册表中搜索已保存的完全限定模式名,并且在给定的模式中做字符串替换。模式字符串被解析之后,它的全名和模式名都存储在注册表中。

下面是一个简单的测试,展示如何使用这个类:

复制代码
package com.navteq.avro.common;
import java.io.File;
import org.junit.Test;
public class AvroUtilsTest {
private static final String schemaDescription =
"{ \n" +
" \"namespace\": \"com.navteq.avro\", \n" +
" \"name\": \"FacebookUser\", \n" +
" \"type\": \"record\",\n" +
" \"fields\": [\n" +
" {\"name\": \"name\", \"type\": [\"string\", \"null\"] },\n" +
" {\"name\": \"num_likes\", \"type\": \"int\"},\n" +
" {\"name\": \"num_photos\", \"type\": \"int\"},\n" +
" {\"name\": \"num_groups\", \"type\": \"int\"} ]\n" +
"}";
private static final String schemaDescriptionExt =
" { \n" +
" \"namespace\": \"com.navteq.avro\", \n" +
" \"name\": \"FacebookSpecialUser\", \n" +
" \"type\": \"record\",\n" +
" \"fields\": [\n" +
" {\"name\": \"user\", \"type\": com.navteq.avro.FacebookUser },\n" +
" {\"name\": \"specialData\", \"type\": \"int\"} ]\n" +
"}";
@Test
public void testParseSchema() throws Exception{
AvroUtils.parseSchema(schemaDescription1);
Schema extended = AvroUtils.parseSchema(schemaDescriptionExt);
System.out.println(extended.toString(true));
}
}

清单 2 AvroUtils 测试

在这个测试中,第一个模式的完全限定名是 com.navteq.avro.FacebookUser,替换正常运行并打印出以下结果:

复制代码
{
"type" : "record",
"name" : "FacebookSpecialUser",
"namespace" : "com.navteq.avro",
"fields" : [ {
"name" : "user",
"type" : {
"type" : "record",
"name" : "FacebookUser",
"fields" : [ {
"name" : "name",
"type" : [ "string", "null" ]
}, {
"name" : "num_likes",
"type" : "int"
}, {
"name" : "num_photos",
"type" : "int"
}, {
"name" : "num_groups",
"type" : "int"
} ]
}
}, {
"name" : "specialData",
"type" : "int"
} ]
}

清单 3 AvroUtilsTest 的执行结果

使用 Apache Avro 实现继承

一种常见的定义数据的方法是通过继承——使用现有的数据定义并添加参数。虽然技术上 Avro 不支持继承 [7] ,但要是实现一个类继承的结构非常简单。

如果我们有一个基类的定义——FacebookUser,如下:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookUser",
"type": "record",
"fields": [
{"name": "name", "type": ["string", "null"] },
{"name": "num_likes", "type": "int"},
{"name": "num_photos", "type": "int"},
{"name": "num_groups", "type": "int"} ]
}

清单 4 Facebook 用户记录的定义

要创建一个 FacebookSpecialUser 定义非常简单,它大概是这样的:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUser",
"type": "record",
"fields": [
{"name": "user", "type": com.navteq.avro.FacebookUser },
{"name": "specialData", "type": "int"}
]
}

清单 5 Facebook 特殊的用户记录的定义

一个特殊的用户定义包含两个字段——Facebook 的用户类型的用户和一个 int 类型的数据字段。

特殊 Facebook 用户的简单测试类如下:

复制代码
package com.navteq.avro.inheritance;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericDatumWriter;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.BinaryEncoder;
import org.apache.avro.io.Decoder;
import org.apache.avro.io.DecoderFactory;
import org.apache.avro.io.Encoder;
import org.apache.avro.util.Utf8;
import org.junit.Before;
import org.junit.Test;
import com.navteq.avro.common.AvroUtils;
public class TestSimpleInheritance {
private Schema schema;
private Schema subSchema;
@Before
public void setUp() throws Exception {
subSchema = AvroUtils.parseSchema(new File("resources/facebookUser.avro"));
schema = AvroUtils.parseSchema(new File("resources/FacebookSpecialUser.avro"));
}
@Test
public void testSimpleInheritance() throws Exception{
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GenericDatumWriter<genericrecord> writer =<br></br> new GenericDatumWriter<genericrecord>(schema);<br></br> Encoder encoder = new BinaryEncoder(outputStream);<p> GenericRecord subRecord1 = new GenericData.Record(subSchema);</p><br></br> subRecord1.put("name", new Utf8("Doctor Who"));<br></br> subRecord1.put("num_likes", 1);<br></br> subRecord1.put("num_photos", 0);<br></br> subRecord1.put("num_groups", 423);<br></br> GenericRecord record1 = new GenericData.Record(schema);<br></br> record1.put("user", subRecord1);<br></br> record1.put("specialData", 1);<p> writer.write(record1, encoder);</p><p> GenericRecord subRecord2 = new GenericData.Record(subSchema);</p><br></br> subRecord2.put("name", new org.apache.avro.util.Utf8("Doctor WhoWho"));<br></br> subRecord2.put("num_likes", 2);<br></br> subRecord2.put("num_photos", 0);<br></br> subRecord2.put("num_groups", 424);<br></br> GenericRecord record2 = new GenericData.Record(schema);<br></br> record2.put("user", subRecord2);<br></br> record2.put("specialData", 2);<p> writer.write(record2, encoder);</p><p> encoder.flush();</p><p> ByteArrayInputStream inputStream =</p><br></br> new ByteArrayInputStream(outputStream.toByteArray());<br></br> Decoder decoder = DecoderFactory.defaultFactory().<br></br> createBinaryDecoder(inputStream, null);<br></br> GenericDatumReader<genericrecord> reader =<br></br> new GenericDatumReader<genericrecord>(schema);<br></br> while(true){<br></br> try{<br></br> GenericRecord result = reader.read(null, decoder);<br></br> System.out.println(result);<br></br> }<br></br> catch(EOFException eof){<br></br> break;<br></br> }<br></br> catch(Exception ex){<br></br> ex.printStackTrace();<br></br> }<br></br> }<br></br> }<br></br>}<a href="#_ftn8_7758" name="_ftnref8_7758">[8]</a></genericrecord></genericrecord></genericrecord></genericrecord>

清单 6 一个特殊的 Facebook 用户的测试类

运行这个测试类产生预期的结果:

复制代码
{"user": {"name": "Doctor Who", "num_likes": 1, "num_photos": 0,
"num_groups": 423}, "specialData": 1}
{"user": {"name": "Doctor WhoWho", "num_likes": 2, "num_photos": 0,
"num_groups": 424}, "specialData": 2}

清单 7 Facebook 特殊用户的测试结果

如果唯一需要的是有包含基础数据和其他参数的记录,此代码工作正常,但它不提供多态性——读取相同记录时,没办法知道到底读的是哪个类型的记录。

使用 ApacheAvro 实现多态性

与谷歌 protocol buffers 不同 [9] ,Avro 不支持可选参数 [10] ,上述继承的实现不适应于多态性的实现——这是由于必须具备特殊的数据参数。幸运的是,Avro 支持联合体,允许省略某些记录的参数。下面的定义可用于创建一个多态的纪录。对于基准纪录,我将使用清单 4 中描述的例子。为了扩展我们将使用以下两个定义:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUserExtension1",
"type": "record",
"fields": [
{"name": "specialData1", "type": "int"}
]
}

清单 8 首条扩展记录的定义

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUserExtension2",
"type": "record",
"fields": [
{"name": "specialData2", "type": "int"}
]
}

清单 9 第二条扩展记录的定义

有了以上两个定义一个多态记录可以定义如下:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUser",
"type": "record",
"fields": [
{"name": "type", "type": "string" },
{"name": "user", "type": com.navteq.avro.FacebookUser },
{"name": "extension1", "type":
[com.navteq.avro.FacebookSpecialUserExtension1, "null"]},
{"name": "extension2", "type":
[com.navteq.avro.FacebookSpecialUserExtension2, "null"]}
]
}

清单 10 Facebook 特殊用户的多态定义

这里扩展 1 和扩展 2 都是可选的且二者皆可。为了使处理更简单,我添加了一个类型字段,可以用来明确定义的记录类型。

下面给出一个更好的多态记录的定义:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUser1",
"type": "record",
"fields": [
{"name": "type", "type": "string" },
{"name": "user", "type": com.navteq.avro.FacebookUser },
{"name": "extension", "type":
[com.navteq.avro.FacebookSpecialUserExtension1,
com.navteq.avro.FacebookSpecialUserExtension2,
"null"]}
]
}

清单 11 Facebook 特殊用户的改进多态定义

下面给出一个多态 Facebook 特殊用户的简单测试类:

复制代码
package com.navteq.avro.inheritance;
<p>package com.navteq.avro.inheritance;</p><p>import java.io.ByteArrayInputStream;</p><br></br>import java.io.ByteArrayOutputStream;<br></br>import java.io.EOFException;<br></br>import java.io.File;<p>import org.apache.avro.Schema;</p><br></br>import org.apache.avro.generic.GenericData;<br></br>import org.apache.avro.generic.GenericDatumReader;<br></br>import org.apache.avro.generic.GenericDatumWriter;<br></br>import org.apache.avro.generic.GenericRecord;<br></br>import org.apache.avro.io.BinaryEncoder;<br></br>import org.apache.avro.io.Decoder;<br></br>import org.apache.avro.io.DecoderFactory;<br></br>import org.apache.avro.io.Encoder;<br></br>import org.apache.avro.io.JsonDecoder;<br></br>import org.apache.avro.io.JsonEncoder;<br></br>import org.apache.avro.util.Utf8;<br></br>import org.junit.Before;<br></br>import org.junit.Test;<p>import com.navteq.avro.common.AvroUtils;</p><p>public class TestInheritance {</p><p> private Schema FBUser;</p><br></br> private Schema base;<br></br> private Schema ext1;<br></br> private Schema ext2;<p> @Before</p><br></br> public void setUp() throws Exception {<p> base = AvroUtils.parseSchema(new File("resources/facebookUser.avro"));</p><br></br> ext1 = AvroUtils.parseSchema(<br></br> new File("resources/FacebookSpecialUserExtension1.avro"));<br></br> ext2 = AvroUtils.parseSchema(<br></br> new File("resources/FacebookSpecialUserExtension2.avro"));<br></br> FBUser = AvroUtils.parseSchema(new File("resources/FacebooklUserInheritance.avro"));<br></br>}<p> @Test</p><br></br> public void testInheritanceBinary() throws Exception{<br></br> ByteArrayOutputStream outputStream = new ByteArrayOutputStream();<br></br> GenericDatumWriter<genericrecord> writer =<br></br> new GenericDatumWriter<genericrecord>(FBUser);<br></br> Encoder encoder = new BinaryEncoder(outputStream);<p> GenericRecord baseRecord = new GenericData.Record(base);</p><br></br> baseRecord.put("name", new Utf8("Doctor Who"));<br></br> baseRecord.put("num_likes", 1);<br></br> baseRecord.put("num_photos", 0);<br></br> baseRecord.put("num_groups", 423);<br></br> GenericRecord FBrecord = new GenericData.Record(FBUser);<br></br> FBrecord.put("type", "base");<br></br> FBrecord.put("user", baseRecord);<p> writer.write(FBrecord, encoder);</p><p> baseRecord = new GenericData.Record(base);</p><br></br> baseRecord.put("name", new Utf8("Doctor WhoWho"));<br></br> baseRecord.put("num_likes", 1);<br></br> baseRecord.put("num_photos", 0);<br></br> baseRecord.put("num_groups", 423);<br></br> GenericRecord extRecord = new GenericData.Record(ext1);<br></br> extRecord.put("specialData1", 1);<br></br> FBrecord = new GenericData.Record(FBUser);<br></br> FBrecord.put("type", "extension1");<br></br> FBrecord.put("user", baseRecord);<br></br> FBrecord.put("extension", extRecord);<p> writer.write(FBrecord, encoder);</p><p> baseRecord = new GenericData.Record(base);</p><br></br> baseRecord.put("name", new org.apache.avro.util.Utf8("Doctor WhoWhoWho"));<br></br> baseRecord.put("num_likes", 2);<br></br> baseRecord.put("num_photos", 0);<br></br> baseRecord.put("num_groups", 424);<br></br> extRecord = new GenericData.Record(ext2);<br></br> extRecord.put("specialData2", 2);<br></br> FBrecord = new GenericData.Record(FBUser);<br></br> FBrecord.put("type", "extension2");<br></br> FBrecord.put("user", baseRecord);<br></br> FBrecord.put("extension", extRecord);<p> writer.write(FBrecord, encoder);</p><p> encoder.flush();</p><p> byte[] data = outputStream.toByteArray();</p><br></br> ByteArrayInputStream inputStream = new ByteArrayInputStream(data);<br></br> Decoder decoder =<br></br> DecoderFactory.defaultFactory().createBinaryDecoder(inputStream, null);<br></br> GenericDatumReader<genericrecord> reader =<br></br> new GenericDatumReader<genericrecord>(FBUser);<br></br> while(true){<br></br> try{<br></br> GenericRecord result = reader.read(null, decoder);<br></br> System.out.println(result);<br></br> }<br></br> catch(EOFException eof){<br></br> break;<br></br> }<br></br> catch(Exception ex){<br></br> ex.printStackTrace();<br></br> }<br></br> }<br></br> }<br></br>}</genericrecord></genericrecord></genericrecord></genericrecord>

清单 12 一条多态 Facebook 用户记录的测试类

运行这个测试类产生的预期结果:

复制代码
{"type": "base", "user": {"name": "Doctor Who", "num_likes": 1, "num_photos":
0, "num_groups": 423}, "extension": null}
{"type": "extension1", "user": {"name": "Doctor WhoWho", "num_likes": 1,
"num_photos": 0, "num_groups": 423}, "extension": {"specialData1": 1}}
{"type": "extension2", "user": {"name": "Doctor WhoWhoWho", "num_likes": 2,
"num_photos": 0, "num_groups": 424}, "extension": {"specialData2": 2}}

清单 13 多态 Facebook 用户记录测试的执行结果

使用 ApacheAvro 的向后兼容性

XML 的优势之一就是当模式定义使用可选参数扩展时具备向后兼容性。我们介绍一个第三扩展记录的定义来测试 Avro 的这个特性:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUserExtension3",
"type": "record",
"fields": [
{"name": "specialData3", "type": "int"}
]
}

清单 14 第三扩展记录的定义

多态记录的变更定义如下:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUser11",
"type": "record",
"fields": [
{"name": "type", "type": "string" },
{"name": "user", "type": com.navteq.avro.FacebookUser },
{"name": "extension", "type":
[com.navteq.avro.FacebookSpecialUserExtension1,
com.navteq.avro.FacebookSpecialUserExtension2,
com.navteq.avro.FacebookSpecialUserExtension3,
"null"]}
]
}

清单 15 Facebook 特殊用户的改进多态定义

为了能读取清单 15 中记录定义中的记录,清单 12 中的代码在修改后(但仍然用清单 11 中的记录定义来写数据)生成下列结果:

复制代码
{"type": "base", "user": {"name": "Doctor Who", "num_likes": 1, "num_photos":
0, "num_groups": 423}, "extension": {"specialData3": 10}}
<u><span color="#0000a0">java.lang.ArrayIndexOutOfBoundsException</span></u>
      <span color="#ff0000">at java.lang.System.arraycopy(<u><span color="#0000a0">Native Method</span></u>)</span>
      <span color="#ff0000">at org.apache.avro.io.BinaryDecoder.doReadBytes(<u><span color="#0000a0">BinaryDecoder.java:331</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.io.BinaryDecoder.readString(<u><span color="#0000a0">BinaryDecoder.java:265</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.io.ValidatingDecoder.readString(<u><span color="#0000a0">ValidatingDecoder.java:99</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.readString(<u><span color="#0000a0">GenericDatumReader.java:318</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.readString(<u><span color="#0000a0">GenericDatumReader.java:312</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.read(<u><span color="#0000a0">GenericDatumReader.java:120</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.readRecord(<u><span color="#0000a0">GenericDatumReader.java:142</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.read(<u><span color="#0000a0">GenericDatumReader.java:114</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.read(<u><span color="#0000a0">GenericDatumReader.java:105</span></u>)<br></br></span>      <span color="#ff0000">at com.navteq.avro.inheritance.TestInheritance.testInheritanceBinary(<u><span color="#0000a0">TestInheritance.java:119</span></u>)<br></br></span>      <span color="#ff0000">at sun.reflect.NativeMethodAccessorImpl.invoke0(<u><span color="#0000a0">Native Method</span></u>)<br></br></span>      <span color="#ff0000">at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)<br></br></span>      <span color="#ff0000">at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)<br></br></span>      <span color="#ff0000">at java.lang.reflect.Method.invoke(Unknown Source)</span>

清单 16 多态 Facebook 用户记录对扩展定义测试的执行结果

虽然 Avro 提供了一个能够解决这个问题的 API——GenericDatumReader构造函数可以使用两个参数——分别用来写记录与读记录的模式,但这不总是解决向后兼容问题的一定可行的方法,因为它必须要记住用来写每条记录的所有模式。

一个更合适的解决方案是:从二进制编码器 / 解码器(它建立记录的二进制表象)切换到 JSON 编码器 / 解码器。在这种情况下代码有效,并产生以下结果:

复制代码
{"type": "base", "user": {"name": "Doctor Who", "num_likes": 1, "num_photos":
0, "num_groups": 423}, "extension": null}
{"type": "extension1", "user": {"name": "Doctor WhoWho", "num_likes": 1,
"num_photos": 0, "num_groups": 423}, "extension": {"specialData1": 1}}
{"type": "extension2", "user": {"name": "Doctor WhoWhoWho", "num_likes": 2,
"num_photos": 0, "num_groups": 424}, "extension": {"specialData2": 2}}

清单 17 应用 JSON 编码多态 Facebook 用户记录对扩展定义测试的执行结果

通过 JSON 的编码器,实际的数据转换成 JSON:

复制代码
{"type":"base","user":{"name":{"string":"Doctor
Who"},"num_likes":1,"num_photos":0,"num_groups":423},"extension":null}
{"type":"extension1","user":{"name":{"string":"Doctor
WhoWho"},"num_likes":1,"num_photos":0,"num_groups":423},"extension":{"FacebookSpecialUserExtension1":{"specialData1":1}}}
{"type":"extension2","user":{"name":{"string":"Doctor
WhoWhoWho"},"num_likes":2,"num_photos":0,"num_groups":424},"extension":{"FacebookSpecialUserExtension2":{"specialData2":2}}}

清单 18 JSON 编码下所转换的数据

还有一个需要考虑的问题,在我的测试中,同样的数据在二进制编码下产生的 Avro 记录的大小为 89 字节,而在 JSON 编码下产生了 473 字节。

结论

当前实现的 Avro 不直接支持模式的组件化或模式组件重用,但像本文中描述的一个简单的框架能够为这些特性提供支持。尽管 Avro 不直接支持多态性,文中利用适当的模式设计可以简单地实现多态数据模式。至于真正意义上向后兼容性问题,只有使用 JSON 编码的时候 Avro 才支持 [11] 。最后一点和 Avro 的特性没有多大关系,更多的是来自 JSON。最后一点严重限制了 Avro 适用性(如果向后兼容性是必须的),使其使用范围局限为一种高级的 JSON 编组和处理 API。

除了一般的(这里所用到的)Avro 方法,也可以使用一个特定的 Avro。这时候,可通过(Avro)生产特定的记录而非普通的记录。尽管有些说法指出 [12] Avro 的特定应用能够获得性能提升,以我使用当前 Avro 版本(1.4.1)的经验来看,两者有着同样的性能表现。


[1] http://hadoop.apache.org/avro/

[2] http://avro.apache.org/docs/1.4.1/

[3] http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

[4] 我在 Avro 编组 Avro Map Reduce 发现的几篇

[5] http://avro.apache.org/docs/current/spec.html

[6] 很有趣,Avro IDL 支持子 IDL

[7] 与明确支持类型定义中的基类型的 XML 不同

[8] 关于上面的代码需要指出的一点是,模式解析是在构造函数中完成的,原因在于构造解析是 Avro 实现中最昂贵的操作。

[9] http://code.google.com/p/protobuf/

[10] Avro 支持“Null”,这不同于可选参数,在 Avro 中“Null”表示某个属性没有值

[11] 或者如果有旧版本的模式

[12] http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

查看英文原文: Using Apache Avro


感谢马国耀对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2011-03-11 00:0022418

评论

发布
暂无评论
发现更多内容

太强了!阿里技术官新产"Spring高级源码阅读指南",爆火全网

Java你猿哥

Java spring SSM框架 spring ioc spring aoc

文盘Rust -- 用Tokio实现简易任务池

京东科技开发者

rust runtime tokio 企业号 4 月 PK 榜

揭开“虚拟化已死”的5大谎言与真相

科技热闻

活久见,java8 lamdba Collectors.toMap()报NPE

做梦都在改BUG

一名开发者眼中的TiDB与MySQL选择

TiDB 社区干货传送门

数据库架构选型

OpenHarmony社区运营报告(2023年3月)

OpenHarmony开发者

OpenHarmony

SLBR通过自校准的定位和背景细化来去除可见的水印

合合技术团队

人工智能 图像处理 水印消除

总结一下Redis的缓存雪崩、缓存击穿、缓存穿透

做梦都在改BUG

全新适配鸿蒙生态,Cocos引擎助力3D应用开发

HarmonyOS开发者

HarmonyOS

华为19级大佬10年心血终成百页负载均衡高并发网关设计实战文档

做梦都在改BUG

Java 负载均衡 高并发 网关设计

SpringBoot 集成 atomikos 实现分布式事务

做梦都在改BUG

更安全、更低耗的微服务架构改造之道

华为云开发者联盟

云计算 后端 华为云 华为云开发者联盟 企业号 4 月 PK 榜

爆肝一月!527页文档详解SpringCloud微服务和分布式系统实践

小小怪下士

Java 分布式 微服务 后端 SpringCloud

【问题解决】解决 swagger2 默认地址失效

Java你猿哥

Java JAVA开发 swagger2 java项目

【4.7-4.14】写作社区优秀技术博文一览

InfoQ写作社区官方

热门活动 优质创作周报

Java中的异常处理详解(try、catch、finally、throw、throws) | 社区征文

共饮一杯无

Java 异常处理 三周年连更

Netty框架详解:高性能网络编程的设计与实现

做梦都在改BUG

网络编程 Netty 高性能

后端开挂!一个接口实现CRUD操作,这款工具绝了!

Java你猿哥

Java 接口 后端 crud

业内首份!医疗数据安全政策汇编发布(附下载)

极盾科技

数据安全

BAT必刷!GitHub顶级“2023并发编程全优笔记”晋升公司架构组!

Java你猿哥

Java 多线程 面经 SSM框架 多线程并发

得帆云DeMDM,业内首家基于低代码技术构建的主数据管理平台

得帆信息

低代码 数据治理 数据清洗 主数据管理 主数据管理平台

HummerRisk V1.0.0:架构全面升级,开启新篇章

HummerCloud

云原生安全

应用部署引起上游服务抖动问题分析及优化实践方案

京东科技开发者

应用部署 jsf 企业号 4 月 PK 榜 上游服务抖动

被裁后,狂刷大牛分享的607页JUC源码分析笔记,立马拿蚂蚁offer

做梦都在改BUG

Java 高并发 JUC JCF 集合框架

从零学习SDK(6)调试和测试SDK的库

MobTech袤博科技

阿里P8架构师爆肝分享内部开源的JVM垃圾回收PDF文档,共23.3W字

做梦都在改BUG

Java JVM 垃圾回收

新手测试必学的 API 接口文档知识

Apifox

测试 入门 接口文档 API API 文档

人人自媒体的时代,程序员该如何利用好自己的优势?我记住了这些神器...

浅羽技术

工具 自媒体 写作技巧 三周年连更

高可靠多层板制造服务再获认可!华秋荣获创想三维优秀质量奖

华秋电子

EMQX Cloud BYOC版本发布:在您的云上体验全托管的MQTT消息服务

EMQ映云科技

物联网 IoT 云服务 mqtt 企业号 4 月 PK 榜

华秋PCB生产工艺 | 第十二道主流程之FQC

华秋电子

使用Apache Avro_SOA_Boris Lublinsky_InfoQ精选文章