如何 0 成本启动全员 AI 技能提升?戳> 了解详情
写点什么

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

评论

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

GIithub/ipq4018/ipq4028/IPQ4019/IPQ4029/ipq4018/ipq4028/QSDK/wifi5 miniPIe/support openwrt

wallysSK

IPQ4019 ipq4029 IPQ4018 IPQ4028

数字孪生可视化技术打造未来智慧码头系统

2D3D前端可视化开发

物联网 智慧港口 数字孪生 智慧码头 5G智慧港口

10个Python脚本来自动化你的日常任务

@下一站

Python 脚本 12月日更 12月月更

软件测试丨让工作更高效,搞定Git的分支管理

测试人

git GitHub 软件测试 自动化测试 测试开发

2022年12月中国数据库排行榜:OceanBase立足创新登榜首,华为腾讯排名上升树雄心

墨天轮

数据库 opengauss TiDB oceanbase 国产数据库

软件测试丨基于Junit4,利用xUnit框架让你的测试用例可维护性大幅提升

测试人

软件测试 单元测试 自动化测试 测试框架 测试开发

什么是BPM系统?BPM流程管理系统介绍

优秀

BPM 业务流程管理

MacBook2023免费的系统电脑优化软件CleanMyMac

茶色酒

CleanMyMac CleanMyMac X CleanMyMac X2023

自助取数、即席分析...瓴羊Quick BI助力企业数字化转型

对不起该用户已成仙‖

Guitar Pro2023吉他软件最新版本安装包下载

茶色酒

Guitar Pro Guitar Pro8

大咖说·施耐德|数智技术,企业绿色低碳转型的催化剂

大咖说

双碳 绿色转型

Karmada多云多集群生产实践专场圆满落幕

华为云开发者联盟

云原生 华为云 12 月 PK 榜

开发小游戏都有哪些难点?

FinFish

小游戏 小游戏开发 小程序游戏 微信小游戏

服开与编排,老兵新传

鲸品堂

电信运营商 12 月 PK 榜

DHorse打包原理

tiandizhiguai

maven 打包工具 构建

瓴羊Quick BI 权限管理:构建高效企业数据分析运行管理体系

夏日星河

如何使用Towify在小程序中配置微信登录

Towify

微信小程序 编辑器 无代码

云服务器代理商选哪家好?理由是什么?

行云管家

云计算 服务器 云服务器

企业级大数据平台智能运维好帮手——星环科技多模数据平台监控软件Aquila Insight

星环科技

如何在 towify 中使用组件切换器配置“点赞”功能?

Towify

微信小程序 编辑器 无代码

掌握分布式环境缓存更新策略,提高缓存与数据库双写一致性!

C++后台开发

数据库 redis 分布式 中间件 后端开发

【知识分享】关于电子元器件封装的几个小知识

华秋PCB

封装 PCB PCB设计

聚焦人机交互智能应用领域,APISIX 在希沃网关的应用与实践

API7.ai 技术团队

api 网关 APISIX 用户案例

多样化数据看板,瓴羊Quick BI满足企业经营管理需求

对不起该用户已成仙‖

小游戏流量变现瓶颈,新增长点是超级App?

FinFish

小游戏 小程序游戏 微信小游戏 小游戏引擎 流量变现

仅用5个小时纯Java版实现羊了个羊,来看源码吧,还送你108套皮肤

非喵鱼

Java swing 小游戏 羊了个羊

matic链佛萨奇系统开发源代码快速部署上线

开发微hkkf5566

企业数字化转型关键路径:构建数据驱动的管控体系

元年技术洞察

数字化转型 数据驱动 方舟平台

等保三级认证备案证明是哪个机构颁发?一般要多久?

行云管家

等保 等级保护 等保三级 等保备案

性能达1.5+倍!昇腾AI助力分子动力学模拟研究

华为云开发者联盟

人工智能 华为云 昇腾AI 12 月 PK 榜

英特尔着眼系统工艺协同优化理念,推进摩尔定律新浪潮

科技之家

使用Apache Avro_SOA_Boris Lublinsky_InfoQ精选文章