在快速入門之前,我們先來瞭解一下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 constructorUser user2 = new User("Ben", 7, "red");// Construct via builderUser user3 = User.newBuilder() .setName("Charlie") .setFavoriteColor("blue") .setFavoriteNumber(null) .build();
序列化
把前面建立的使用者序列化並儲存到磁碟檔案:
// Serialize user1, user2 and user3 to diskDatumWriter<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 diskDatumReader<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, 是不是很簡單。