原文地址: https://itweknow.cn/detail?id=70 ,歡迎大家訪問。
什么是Avro
Avro是一個獨立于編程語言的數(shù)據(jù)序列化系統(tǒng)。這個項目由Ddoug Cutting(Hadoop之父)創(chuàng)建,目標是解決Hadoop中Writable類型缺乏語言的可移植性的不足。Avro模式通常采用JSON來寫,數(shù)據(jù)則采用二進制格式編碼,也可采用基于JSON的數(shù)據(jù)編碼方式。
Avro的數(shù)據(jù)類型和模式
Avro定義了一些基本的數(shù)據(jù)類型,我們可以用他們來構(gòu)建應用特定的數(shù)據(jù)結(jié)構(gòu)。下面的表格我們列舉了Avro的基本類型。
| 類型 | 描述 | 模式示例 |
|---|---|---|
| null | 空值 | "null" |
| boolean | 二進制值 | "boolean" |
| int | 32位帶符號整數(shù) | "int" |
| long | 64位帶符號整數(shù) | "long" |
| float | 單精度(32位)浮點數(shù) | "float" |
| double | 雙精度(64位)浮點數(shù) | "double" |
| bytes | 8位無符號字節(jié)序列 | "bytes" |
| string | Unicode字符序列 | "string" |
還有一些復雜的類型如下表所示:
| 類型 | 描述 | 模式示例 |
|---|---|---|
| array | 一個排過序的對象集合。特定數(shù)組中的所有對象必須模式相同。 | {"type": "array","items": "long"} |
| map | 未排過序的鍵-值對。鍵必須是字符串,值可以是任何一種類型,但是某一個map內(nèi)的所有值必須模式相同。 | {"type": "map", "values": "string"} |
| record | 一個任意類型的命名字段集合。(相當于java中的自定義對象) | {"type": "record", "name": "User", "doc":"A User Desc","fileds":[{"name":"nickname","type": "string"},{"name":"age","type":"int"}]} |
| enum | 一個命名的值集合(枚舉) | {"type":"enum","name":"ActionStatus","doc":"操作狀態(tài)","symbols":["SUCCESS","FAILED","ACTING"]} |
| fixed | 一組固定數(shù)量的8位無符號字節(jié) | {"type":"fixed","name":"Md5Hash","size":16} |
| union | 模式的并集。并集可用JSON數(shù)組表示,其中每個元素為一個模式。并集表示的數(shù)據(jù)必須與其內(nèi)的某個模式相匹配 | ["null","string",{"type":"map","values":"string"}] |
Avro數(shù)據(jù)文件
前面也提到過設計Avro的目的就是解決Hadoop中Writable類型缺乏語言的可移植性的不足。Avro數(shù)據(jù)文件主要是面向跨語言使用而設計的,我們可以通過Java寫入文件,然后通過Python來讀取文件,這都是沒有問題的。數(shù)據(jù)文件的頭部包含一個Avro模式和一個同步標識(sync marker),然后緊接著是一系列包含序列化Avro對象的數(shù)據(jù)塊。數(shù)據(jù)塊通過sync marker分隔。
這里有兩個概念解釋一下:
- Avro模式 - 其實就相當于對象的定義,在這里我們規(guī)定字段的類型以及描述等信息。
- sync marker - 對與該文件來講是唯一的,存儲在文件頭部。
序列化和反序列化
上面也簡單的了解了一下Avro,下面我們通過兩段代碼來嘗試一下Avro的序列化和反序列化。
- Avro模式定義,User.avsc
{
"type": "record",
"name": "User",
"doc": "一個用戶",
"fields": [
{"name": "name", "type": "string"},
{"name": "age", "type": "int"}
]
}
- 序列化
@Test
public void write() throws IOException {
Schema.Parser parser = new Schema.Parser();
InputStream in = this.getClass().getResourceAsStream("User.avsc");
Schema schema = parser.parse(in);
GenericRecord record = new GenericData.Record(schema);
record.put("name", "ganchaoyang");
record.put("age", 23);
File file = new File("result.avro");
DatumWriter<GenericRecord> writer = new GenericDatumWriter<GenericRecord>(schema);
try(DataFileWriter<GenericRecord> dataFileWriter = new DataFileWriter<GenericRecord>(writer)) {
dataFileWriter.create(schema, file);
dataFileWriter.append(record);
}
}
- 反序列化
@Test
public void read() throws IOException {
File file = new File("result.avro");
DatumReader<GenericRecord> reader = new GenericDatumReader<>();
try (DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(file, reader)) {
GenericRecord record;
while (dataFileReader.hasNext()) {
record = dataFileReader.next();
Assert.assertEquals("ganchaoyang", record.get("name").toString());
Assert.assertEquals(23, record.get("age"));
}
}
}