Avro([?vr?])是Hadoop的一個(gè)子項(xiàng)目,由Hadoop的創(chuàng)始人Doug Cutting(也是Lucene涣狗,Nutch等項(xiàng)目的創(chuàng)始人)牽頭開發(fā)。Avro是一個(gè)數(shù)據(jù)序列化系統(tǒng)舒憾,設(shè)計(jì)用于支持大批量數(shù)據(jù)交換的應(yīng)用镀钓。它的主要特點(diǎn)有:支持二進(jìn)制序列化方式,可以便捷镀迂,快速地處理大量數(shù)據(jù)丁溅;動(dòng)態(tài)語(yǔ)言友好,Avro提供的機(jī)制使動(dòng)態(tài)語(yǔ)言可以方便地處理Avro數(shù)據(jù)探遵。
一窟赏、數(shù)據(jù)序列化/反序列化(data serialization/deserialization)
Avro支持兩種序列化編碼方式:二進(jìn)制編碼和JSON編碼。使用二進(jìn)制編碼會(huì)高效序列化箱季,并且序列化后得到的結(jié)果會(huì)比較醒那睢;而JSON一般用于調(diào)試系統(tǒng)或是基于WEB的應(yīng)用藏雏。對(duì)Avro數(shù)據(jù)序列化/反序列化時(shí)都需要對(duì)模式以深度優(yōu)先(Depth-First)拷况,從左到右(Left-to-Right)的遍歷順序來(lái)執(zhí)行。
Avro依賴模式(Schema)來(lái)實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)定義掘殴∽荩可以把模式理解為Java的類,它定義每個(gè)實(shí)例的結(jié)構(gòu)奏寨,可以包含哪些屬性起意。可以根據(jù)類來(lái)產(chǎn)生任意多個(gè)實(shí)例對(duì)象服爷。對(duì)實(shí)例序列化操作時(shí)必須需要知道它的基本結(jié)構(gòu)杜恰,也就需要參考類的信息。這里仍源,根據(jù)模式產(chǎn)生的Avro對(duì)象類似于類的實(shí)例對(duì)象心褐。每次序列化/反序列化時(shí)都需要知道模式的具體結(jié)構(gòu)。所以笼踩,在Avro可用的一些場(chǎng)景下逗爹,如文件存儲(chǔ)或是網(wǎng)絡(luò)通信,都需要模式與數(shù)據(jù)同時(shí)存在。Avro數(shù)據(jù)以模式來(lái)讀和寫(文件或是網(wǎng)絡(luò))掘而,并且寫入的數(shù)據(jù)都不需要加入其它標(biāo)識(shí)挟冠,這樣序列化時(shí)速度快且結(jié)果內(nèi)容少。由于程序可以直接根據(jù)模式來(lái)處理數(shù)據(jù)袍睡,所以Avro更適合于腳本語(yǔ)言的發(fā)揮知染。
需要的Jar包依賴
avro-1.7.3.jar,avro-tools-1.7.3.jar斑胜,jackson-core-asl-1.9.3.jar控淡,jackson-mapper-asl-1.9.3.jar定義模式(Schema)
在avro中,它是用Json格式來(lái)定義模式的止潘。模式可以由基礎(chǔ)類型(null, boolean, int, long, float, double, bytes, and string)和復(fù)合類型(record, enum, array, map, union, and fixed)的數(shù)據(jù)組成掺炭。這里定義了一個(gè)簡(jiǎn)單的模式user.avsc:
{
"namespace": "com.zq.avro",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]}
]
}
上面的模式是定義了一個(gè)用戶的記錄,在模式定義中凭戴,必須包含它的類型("type": "record")涧狮、一個(gè)名字("name": "User")以及fields。在本例中fields包括了name, favorite_number和favorite_color么夫,上面的模式我們還定義了一個(gè)命名空間 ("namespace": "com.zq.avro"),namespace可以名字一起使用者冤,從而組成模式的全名(本例為com.zq.avro.User)。
-
編譯模式(compile schema)
Avro可以允許我們根據(jù)模式的定義而生成相應(yīng)的類魏割,一旦我們定義好相關(guān)的類譬嚣,程序中就不需要直接使用模式了〕可以用avro-tools jar包根據(jù)user.avsc生成User.java拜银,語(yǔ)法如下:
java -jar avro-tools-1.7.4.jar compile schema . [注意這里有第三個(gè)參數(shù)"."]
命令執(zhí)行后會(huì)在當(dāng)前目錄根據(jù)設(shè)定的包結(jié)構(gòu)生成一個(gè)User.java類,然后就可以將定義的User對(duì)象用avro將它序列化存放到本地文件中遭垛,再將其反序列化尼桶。
- 編寫Java代碼
public static void main(String[] args) throws Exception {
User user1 = new User();
user1.setName("Arway");
user1.setFavoriteNumber(3);
user1.setFavoriteColor("green");
User user2 = new User("Ben", 7, "red");
// construct with builder
User user3 = User.newBuilder().setName("Charlie").setFavoriteColor("blue").setFavoriteNumber(100).build();
// Serialize user1, user2 and user3 to disk
File file = new File("C:\\Users\\kimibob\\Desktop\\users.avro");
DatumWriter<User> userDatumWriter = new SpecificDatumWriter<User>(User.class);
DataFileWriter<User> dataFileWriter = new DataFileWriter<User>(userDatumWriter);
try {
dataFileWriter.create(user1.getSchema(), file);
dataFileWriter.append(user1);
dataFileWriter.append(user2);
dataFileWriter.append(user3);
dataFileWriter.close();
} catch (IOException e) {
}
// Deserialize Users from dist
DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class);
DataFileReader<User> dataFileReader = null;
try {
dataFileReader = new DataFileReader<User>(file, userDatumReader);
} catch (IOException e) {
}
User user = null;
try {
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);
}
} catch (IOException e) {
}
}
運(yùn)行完這段代碼之后,將會(huì)在磁盤產(chǎn)生users.avro文件锯仪,里面是用avro序列化user的二進(jìn)制數(shù)據(jù)泵督,再對(duì)其進(jìn)行反序列化,在控制臺(tái)輸出文本json格式的數(shù)據(jù)庶喜。
{"name": "Arway", "favorite_number": 3, "favorite_color": "green"}
{"name": "Ben", "favorite_number": 7, "favorite_color": "red"}
{"name": "Charlie", "favorite_number": 100, "favorite_color": "blue"}
對(duì)比可以看出序列化后的avro格式文件大小遠(yuǎn)小于文本格式小腊,有利于節(jié)省網(wǎng)絡(luò)傳輸?shù)拈_銷。
二久窟、Avro RPC框架
- RPC邏輯上分為二層秩冈,一是傳輸層,負(fù)責(zé)網(wǎng)絡(luò)通信斥扛;二是協(xié)議層入问,將數(shù)據(jù)按照一定協(xié)議格式打包和解包
- 從序列化方式來(lái)看,Apache Thrift 和Google的Protocol Buffers和Avro應(yīng)該是屬于同一個(gè)級(jí)別的框架,都能跨語(yǔ)言芬失,性能優(yōu)秀楣黍,數(shù)據(jù)精簡(jiǎn),但是Avro的動(dòng)態(tài)模式(不用生成代碼棱烂,而且性能很好)這個(gè)特點(diǎn)讓人非常喜歡租漂,比較適合RPC的數(shù)據(jù)交換。
- Avro RPC 是一個(gè)支持跨語(yǔ)言實(shí)現(xiàn)的RPC服務(wù)框架垢啼。非常輕量級(jí)窜锯,實(shí)現(xiàn)簡(jiǎn)潔,使用方便芭析,同時(shí)支持使用者進(jìn)行二次開發(fā),邏輯上該框架分為兩層:
1.網(wǎng)絡(luò)傳輸層使用Netty的Nio實(shí)現(xiàn)吞瞪。
2.協(xié)議層可擴(kuò)展馁启,目前支持的數(shù)據(jù)序列化方式有Avro, Protocol Buffers, Json, Hessian,Java序列化。 使用者可以注冊(cè)自己的協(xié)議格式及序列化方式芍秆。
上面是將Avro對(duì)象序列化到文件的操作惯疙。與之相應(yīng)的,Avro也被作為一種RPC框架來(lái)使用妖啥∶沟撸客戶端希望同服務(wù)器端交互時(shí),就需要交換雙方通信的協(xié)議荆虱,它類似于模式蒿偎,需要雙方來(lái)定義,在Avro中被稱為消息(Message)怀读。通信雙方都必須保持這種協(xié)議诉位,以便于解析從對(duì)方發(fā)送過(guò)來(lái)的數(shù)據(jù),這也就是傳說(shuō)中的握手(handshake)階段菜枷。
Avro RPC開發(fā)
-
需要的Jar包依賴
avro-1.7.3.jar苍糠, avro-ipc-1.7.3.jar, netty-3.5.12.Final.jar啤誊, slf4j-api-1.6.1.jar - 定義協(xié)議模式(protocol Schema)
{"namespace": "com.zq.avro",
"protocol": "Mail",
"types": [
{"name": "Message", "type": "record",
"fields": [
{"name": "to", "type": "string"},
{"name": "from", "type": "string"},
{"name": "body", "type": "string"}
]
}
],
"messages": {
"send": {
"request": [{"name": "message", "type": "Message"}],
"response": "string"
}
}
}
- 編譯模式(compile schema)
java -jar avro-tools-1.7.3.jar compile protocol mail.avpr . [注意這里有第三個(gè)參數(shù)"."]
命令執(zhí)行后會(huì)在當(dāng)前目錄根據(jù)設(shè)定的包結(jié)構(gòu)生成一個(gè)Mail接口和一個(gè)Message類岳瞭。
-
編寫Java代碼
AvroServer類:
class MailImpl implements Mail {
@Override
public CharSequence send(Message message) throws AvroRemoteException {
System.out.println("Message Received:" + message);
return new Utf8("Received your message: " + message.getFrom().toString() + " with body "
+ message.getBody().toString());
}
}
public class AvroServer {
private static Server server;
public static void main(String[] args) throws Exception {
System.out.println("Starting server");
startServer();
Thread.sleep(1000);
System.out.println("Server started");
Thread.sleep(60 * 1000);
server.close();
}
private static void startServer() throws IOException {
server = new NettyServer(new SpecificResponder(Mail.class, new MailImpl()), new InetSocketAddress(65111));
}
}
AvroClient類:
public class AvroClient {
public static void main(String[] args) throws Exception {
NettyTransceiver client = new NettyTransceiver(new InetSocketAddress(65111));
/// 獲取Mail接口的proxy實(shí)現(xiàn)
Mail proxy = SpecificRequestor.getClient(Mail.class, client);
System.out.println("Client of Mail Proxy is built");
// fill in the Message record and send it
args = new String[] { "to:Tom", "from:Jack", "body:How are you" };
Message message = new Message();
message.setTo(new Utf8(args[0]));
message.setFrom(new Utf8(args[1]));
message.setBody(new Utf8(args[2]));
System.out.println("RPC call with message: " + message.toString());
/// 底層給服務(wù)器發(fā)送send方法調(diào)用
System.out.println("Result: " + proxy.send(message));
// cleanup
client.close();
}
}
擴(kuò)展:
- Netty是什么?
本質(zhì):JBoss做的一個(gè)Jar包
目的:快速開發(fā)高性能蚊锹、高可靠性的網(wǎng)絡(luò)服務(wù)器和客戶端程序
優(yōu)點(diǎn):提供異步的瞳筏、事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架和工具
通俗的說(shuō):Netty是一個(gè)NIO的框架,可以用于開發(fā)分布式的Java程序- 如果沒(méi)有Netty枫耳?
遠(yuǎn)古:java.net + java.io
近代:java.nio
其他:Mina乏矾,Grizzly