HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

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

评论

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

SAP ABAP 关键字语法图和 ABAP 代码自动生成工具 Code Composer

汪子熙

Java SAP abap commerce 8月月更

IPv5是什么意思?到底有没有IPv5?

郑州埃文科技

ipv6 互联网协议 ipv5

java就业培训班如何选择?

小谷哥

表达式转换-中缀转后缀表达式后计算-数据结构与算法

清风莫追

算法与数据结构 8月月更

2022年中国足球赛事营销洞察

易观分析

营销 报告 足球

华贵保险连续五年获得“A级纳税信用企业”

江湖老铁

Go-Excelize API源码阅读(十)—— SetActiveSheet(index int)

Regan Yue

Go 开源 源码阅读 8月日更 8月月更

Alluxio on Amazon EMR 集成实践

亚马逊云科技 (Amazon Web Services)

实践 集成 Tech 专栏

计算机专业和培训出来的前端程序员的区别

小谷哥

前端培训学完课程后如何找工作?

小谷哥

终于!极狐GitLab 支持 ARM 啦!

极狐GitLab

DevOps 敏捷开发 arm 极狐GitLab 嵌入式硬件

史上最全的Java并发系列之并发编程的挑战

自然

多线程 并发 8月月更

开源一夏 | TypeScript对于Duck类型和模块命名空间的应用实战

恒山其若陋兮

开源 8月月更

迄今为止把Mybatis讲解的最详细的PDF,图文并茂,通俗易懂

冉然学Java

Java 编程 程序员 mybatis 构架

面试的朋友听我说,18个MyBatis高频知识及学习笔记,双手奉上

冉然学Java

Java 源码 分布式 mybatis 构架

CSO视角:Sigstore如何保障软件供应链安全?

SEAL安全

软件供应链安全 OpenSSF

预约直播 | 深度学习编译器技术趋势与阿里云BladeDISC的编译器实践

阿里云大数据AI技术

深度学习 阿里云 编译器

二分查找:一种效率较高的查找方法

华为云开发者联盟

开发 二分查找 查找 区间

合合信息加入元脑生态 携手浪潮信息推动金融AI应用创新

合合技术团队

AI 智能时代 合合信息 人工智能’ 浪潮

大数据培训学习软件工程师机构靠谱吗

小谷哥

开源公开课丨ChunJun数据传输模块介绍

袋鼠云数栈

秒云获得阿里云首批产品生态集成认证,携手共建云原生智能运维生态服务

MIAOYUN

智能运维 智能运维AIOps 阿里云产品生态集成认证

破解双中台困局:万家数科 x StarRocks 数字化技术实践

StarRocks

大数据

过等保费用包含哪些?大概多少钱?

行云管家

等保 等级保护 过等保

2分钟一图看懂AntDB数据库产品

亚信AntDB数据库

AntDB 国产数据库 aisware antdb

史上最全的Java并发系列之Java并发容器和框架

自然

多线程 并发 8月月更

史上最全的Java并发系列之Java并发机制的底层实现原理

自然

多线程 并发 8月月更

【云原生】Docker 进阶 -- 数据卷使用与实战练习

Bug终结者

Docker 阿里云 云原生 8月月更

大数据程序员参加培训好还是自学好

小谷哥

这次6张图带你彻底搞懂RocketMQ是怎么保存偏移量的?

程序员小毕

Java 程序员 面试 RocketMQ 消息中间件

旺链科技成为湖南省区块链协会理事单位

旺链科技

区块链 区块链技术 产业区块链

使用Apache Avro_SOA_Boris Lublinsky_InfoQ精选文章