由於Avro 將在MapReduce V2(YARN)上被使用,所以小弟也來研究一下Avro的一些觀念與實作方式囉^_^~,當然MapReduce V1也可以用到拉。

做為Hadoop資料的序列化(Serialize)與反序列化(Deserialize),比起傳統的Writable,Avro使用來改進Writable的缺點: 可攜性的問題(Writable序列化後的資料無法讓其他程式語言使用)。

當然不僅Avro做得到,還有像是Apache的Thrift、google的protocal buffer也都可以,我個人認為(純粹個人認為),Doug先生使用Avro而不使用其他的方式來取代傳統的Writable或許是因為他對Avro的掌握程度遠比其他的方式還高。

Avro的幾個重點 : 

- Schema(綱要) 是用JSON來寫。

- 嚴格定義二進位格式,也是因為這樣讓其與其他程式語言的聯繫門檻降低。

- 擁有豐富的schema解析,資料寫入與讀取的schema可以不同,這讓schema可以一直演變,這非常屌喔!~

- 規範了"物件容器格式"用來儲存物件序列,這個跟hadoop的循序檔很類似,重點是"Avro資料檔案支援壓縮與分割",可成為MapReduce的資料輸入,這才是屌到爆的地方。

接下來就是要介紹一下Avro的資料型別與綱要。

Avro的資料型別: "基本型別",舉個例子

型別             schema 

null              "null"

boolean       "boolean"

int                "int"

string           "string"

Avro的資料型別: "複雜型別",舉個例子

型別             schema

record         {

                   "type": "record"

                   "name": "test"

                   "fields": [

                                 {"name": "Tony", "type": "string"},

                                 {"name": "Emma", "type": "string"}

                                 ]

                    }

因為Avro是用JSON來寫,所以在寫入或讀取的時候必須告知型別,由上述簡單的帶出,接下來就是說明一下Avro如何做序列化與反序列化的部分。

Avro 有三種方式來達成

- 1. 使用code generation(可參考avro的doc,http://apache.cdpa.nsysu.edu.tw/avro/avro-1.7.3/)。

- 2. 不需使用code generation,而是用Schema parser(照理說這個應該是最常用的),又分成下面2-1與2-2兩種方式來做。

    - 2-1. 在memory中做序列化與反序列化。

    - 2-2. 序列化與反序列化於硬碟的操作。

- 3. 使用Maven(這個部分我沒有用過,有興趣的同學可以用用看唷^.^)。

================無聊分隔線====================

A. code generation

1. 在做Avro的序列化之前,必須先define好schema,這裡直接拿書上的例子來跑!~。

{"namespace": "example.avro",
  "type": "record",
  "name": "User",
  "fields": [
    {"name": "name", "type": "string"},
    {"name": "favorite_number", "type": ["int", "null"]},
    {"name": "favorite_color", "type": ["string", "null"]}
    ]
}

註:order的部分乃是用來排序使用,預計將於觀念篇(2) + MapReduce中會用到。

2. compile schema

java -jar /path/to/avro-tools-1.7.3.jar compile schema <schema file> <destination>

ex: java -jar /path/to/avro-tools-1.7.3.jar compile schema user.avsc .   (貼心小語,destination是一個 . ,也就是當前路徑,別忘囉)

compile後會在當前路徑產生一個目錄 example/avro/User.java (在這裡就能說明為什麼取名是code generation)。

3. 接下來就是把上述example/avro/User.java加到自己的java包中,不然下面的例子會build不過。

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();

4. 序列化後的資料放到硬碟。

// Serialize user1 and user2 to disk
File file = new File("users.avro");
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();

5. 反序列化硬碟中的資料

// Deserialize Users from disk
DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class);
DataFileReader<User> dataFileReader = new DataFileReader<User>(file, 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);
}

跑上面的code做反序列化後可以看到下述結果

{"name": "Alyssa", "favorite_number": 256, "favorite_color": null}
{"name": "Ben", "favorite_number": 7, "favorite_color": "red"}
{"name": "Charlie", "favorite_number": null, "favorite_color": "blue"}

好啦,到這邊我已經快吐了,code generation的方式看起來好不實際=0=,於下篇中將會介紹without code generation的做法,BTW. 這邊的做法是屬於2-2. 序列化與反序列化於硬碟的操作。

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 TonyMoMo 的頭像
    TonyMoMo

    TonyMoMo的部落格

    TonyMoMo 發表在 痞客邦 留言(0) 人氣()