阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

使用 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:0022413

评论

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

架构师训练营第 5 周学习总结

Season

缓存 反向代理 消息队列

架构师训练营 No.5 周总结

连增申

week05 作业

Safufu

缓存、异步、集群和分布式等架构模式的实践

dony.zhang

负载均衡 缓存 消息队列 分布式缓存 异步

一致性Hash算法

羽球

一致性hash

架构师训练营 第五周 作业

亮灯

管理堆内存,Rust是怎么做的?所有权!

袁承兴

c++ rust 堆内存管理 内存管理 垃圾回收

第五周总结

Linuxer

极客大学架构师训练营

区块链重塑供应链

CECBC

架构课第5课作业

张瑞浩

week5-作业 一致性 hash 算法

a晖

week05学习总结

Safufu

架构师训练营 - 作业 - 第五讲

吕浩

架构师训练营 第五周 分布式理论知识分享1

极客

常用分布式组件

张瑞浩

架构师训练营 第五周 【作业】

小K

第五周总结

叶鹏

「架构师训练营」第 5 周总结

guoguo 👻

极客大学架构师训练营

一致性HASH的golang实现

麻辣

架构师训练营作业 (第五周)

小遵

架构师训练营 - 第五周 - 学习总结

Anrika

极客大学架构师训练营

架构师训练营 第五周 作业

极客

架构师训练营第五周学习总结

Bruce Xiong

分布式缓存架构

chenzt

架构师训练营-第五章-课程总结

而立

极客大学架构师训练营

架构师训练营第 0 期第5周作业

无名氏

架构师训练营week05 summary

Nick

极客大学架构师训练营

2020-07-04-第五周作业

路易斯李李李

架构师训练营Week5学习总结

小高

架构师训练营第 5 周作业

Season

一致性哈希 一致性Hash算法

架构师训练营 No.5 周作业

连增申

使用Apache Avro_SOA_Boris Lublinsky_InfoQ精选文章