在快速入門之前,我們先來了解一下Apache Avro到底是什麼東東?能夠用來做什麼?
Apache Avro是一個數據序列化系統。序列化就是將對象轉換成二進制流,相應的反序列化就是將二進制流再轉換成對應的對象。因此,Avro就是用來在傳輸數據之前,將對象轉換成二進制流,然後此二進制流達到目標地址後,Avro再將二進制流轉換成對象。
接下來,我們看看官方網站上是怎麼說的。
Apache Avro是一個數據序列化系統。
Avro提供:
- 豐富的數據結構
- 一個緊湊的,快速的,二進制的數據格式
- 一個容器文件,來存儲持久化數據
- 遠程過程調用(RPC)
- 簡單的動態語言集成。
- 代碼生成不需要讀寫數據文件,也不要使用或實現RPC協議。代碼生成是作爲一個可選的優化,只對靜態類型的語言值得實現。
大家知道,JSON是一種輕量級的數據傳輸格式,對於大數據集,JSON數據會顯示力不從心,因爲JSON的格式是key:value型,每條記錄都要附上key的名字,有的時候,光key消耗的空間甚至會超過value所佔空間,這對空間的浪費十分嚴重,尤其是對大型數據集來說,因爲它不僅不夠緊湊,還要重複地加上key信息,不僅會造成存儲空間上的浪費,更會增加了數據傳輸的壓力,從而給集羣增加負擔,進而影響整個集羣的吞吐量。而採用Avro數據序列化系統可以比較好的解決此問題,因爲用Avro序列化後的文件由schema和真實內容組成,schema只是數據的元數據,相當於JSON數據的key信息,schema單獨存放在一個JSON文件中,這樣一來,數據的元數據只存了一次,相比JSON數據格式的文件,大大縮小了存儲容量。從而使得Avro文件可以更加緊湊地組織數據。
接下來,我們開始使用Avro。
下載
以Maven爲例,增加Avro的依賴,及插件,插件的好處在於,可以直接自動地爲avsc文件生成類。
<dependencies>
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro</artifactId>
<version>1.8.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.8.1</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>
<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
值得注意的是:以上pom文件配置了自動生成類的路徑,即${project.basedir}/src/main/avro/
和${project.basedir}/src/main/java/
,這樣配置之後,在執行mvn命令的時候,這個插件就會自動將此目錄下的avsc schema生成類文件,並放到後者這個目錄下。
定義schema
使用JSON爲Avro定義schema。schema由基本類型(null,boolean, int, long, float, double, bytes 和string)和複雜類型(record, enum, array, map, union, 和fixed)組成。例如,以下定義一個user的schema,在main目錄下創建一個avro目錄,然後在avro目錄下新建文件 user.avsc :
{"namespace": "lancoo.ecbdc.pre",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]}
]
}
用代碼生成來序列化和反序列化
編譯schema
在這裏,因爲使用avro插件,所以,直接輸入以下命令,maven插件會自動幫我們生成類文件:
mvn clean install
然後在剛纔配置的目錄下就會生成相應的類,如下:
如果不使用插件,也可以使用avro-tools來生成:
java -jar /path/to/avro-tools-1.8.1.jar compile schema <schema file> <destination>
創建用戶
在前面,類文件已經創建好了,接下來,可以使用剛纔自動生成的類來創建用戶了:
User user1 = new User();
user1.setName("Alyssa");
user1.setFavoriteNumber(256);
// Leave favorite color null
// Alternate constructor
User user2 = new User("Ben", 7, "red");
// Construct via builder
User user3 = User.newBuilder()
.setName("Charlie")
.setFavoriteColor("blue")
.setFavoriteNumber(null)
.build();
序列化
把前面創建的用戶序列化並存儲到磁盤文件:
// Serialize user1, user2 and user3 to disk
DatumWriter<User> userDatumWriter = new SpecificDatumWriter<User>(User.class);
DataFileWriter<User> dataFileWriter = new DataFileWriter<User>(userDatumWriter);
dataFileWriter.create(user1.getSchema(), new File("users.avro"));
dataFileWriter.append(user1);
dataFileWriter.append(user2);
dataFileWriter.append(user3);
dataFileWriter.close();
這裏,我們是序列化user到文件users.avro
反序列化
接下來,我們對序列化後的數據進行反序列化:
// Deserialize Users from disk
DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class);
DataFileReader<User> dataFileReader = new DataFileReader<User>(new File("users.avro"), userDatumReader);
User user = null;
while (dataFileReader.hasNext()) {
// Reuse user object by passing it to next(). This saves us from
// allocating and garbage collecting many objects for files with
// many items.
user = dataFileReader.next(user);
System.out.println(user);
}
整個創建avro schema,代碼生成,創建用戶,序列化用戶對象,反序列化及最後的輸出結果,完整的代碼可以組織爲以下(在這裏,我使用的是JUNIT):
import org.apache.avro.file.DataFileReader;
import org.apache.avro.file.DataFileWriter;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.specific.SpecificDatumReader;
import org.apache.avro.specific.SpecificDatumWriter;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
/**
* Created by yang on 12/23/16.
*/
public class TestUser {
@Test
public void testCreateUserClass() throws IOException {
User user1 = new User();
user1.setName("Alyssa");
user1.setFavoriteNumber(256);
// Leave favorite color null
// Alternate constructor
User user2 = new User("Ben", 7, "red");
// Construct via builder
User user3 = User.newBuilder()
.setName("Charlie")
.setFavoriteColor("blue")
.setFavoriteNumber(null)
.build();
// Serialize user1, user2 and user3 to disk
DatumWriter<User> userDatumWriter = new SpecificDatumWriter<User>(User.class);
DataFileWriter<User> dataFileWriter = new DataFileWriter<User>(userDatumWriter);
dataFileWriter.create(user1.getSchema(), new File("users.avro"));
dataFileWriter.append(user1);
dataFileWriter.append(user2);
dataFileWriter.append(user3);
dataFileWriter.close();
// Deserialize Users from disk
DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class);
DataFileReader<User> dataFileReader = new DataFileReader<User>(new File("users.avro"), userDatumReader);
User user = null;
while (dataFileReader.hasNext()) {
// Reuse user object by passing it to next(). This saves us from
// allocating and garbage collecting many objects for files with
// many items.
user = dataFileReader.next(user);
System.out.println(user);
}
}
}
代碼執行之後,可以發現,創建了文件users.avro。
輸出結果爲:
{"name": "Alyssa", "favorite_number": 256, "favorite_color": null}
{"name": "Ben", "favorite_number": 7, "favorite_color": "red"}
{"name": "Charlie", "favorite_number": null, "favorite_color": "blue"}
okay, 是不是很簡單?