1.下载与安装
官方网站:http://avro.apache.org/
下载地址:http://labs.renren.com/apache-mirror//avro/avro-1.5.1/avro-src-1.5.1.tar.gz
安装之前确保已经装了maven
cd /usr/local/src
wget http://labs.renren.com/apache-mirror//avro/avro-1.5.1/avro-src-1.5.1.tar.gz
tar zxvf avro-src-1.5.1.tar.gz
cd avro-src-1.5.1/lang/java
mvn clean install -DskipTests
安装后,avro-1.5.1.jar位于avro-src-1.5.1/lang/java/avro/target
2.消息结构与服务接口
Avro的模式主要由JSON对象来表示,Avro支持8种基本类型(Primitive Type)和6种复杂类型(Complex Type:records、enums、arrays、maps、unions 和fixed),基本类型可以由JSON字符串来表示。
Avro支持两种序列化编码方式:二进制编码和JSON编码,使用二进制编码会高效序列化,并且序列化后得到的结果会比较小。
基本类型:
null: no value
boolean: a binary value
int: 32-bit signed integer
long: 64-bit signed integer
float: single precision (32-bit) IEEE 754 floating-point number
double: double precision (64-bit) IEEE 754 floating-point number
bytes: sequence of 8-bit unsigned bytes
string: unicode character sequence
首先编写一个message.avpr文件,定义一个消息结构。
{
"namespace": "avro",
"protocol": "messageProtocol",
"doc": "This is a message.",
"name": "Message",
"types": [
{"name":"message", "type":"record",
"fields":[
{"name":"name", "type":"string"},
{"name":"type", "type":"int"},
{"name":"price", "type":"double"},
{"name":"valid", "type":"boolean"},
{"name":"content", "type":"bytes"}
]}
],
"messages": {
"sendMessage":{
"doc" : "test",
"request" :[{"name":"message","type":"message" }],
"response" :"message"
}
}
}
其中定义了1种类型叫做message,有5个成员name、type、price、valid、content。还定义了1个消息服务叫做sendMessage,输入有一个参数,类型是message,返回message。
3.序列化
Avro有两种序列化编码:binary和JSON。
3.1.Binary Encoding
基本类型:
null:0字节
boolean:1个字节——0(false)或1(true)
int和long使用变长的zig-zag编码
float:4个字节
double:8个字节
bytes:1个long,后边跟着字节序列
string:1个long,后边跟着UTF-8编码的字符
3.2.records
按字段声明的顺序编码值,如下面一个record schema:
{
"type": "record",
"name": "test",
"fields" : [
{"name": "a", "type": "long"},
{"name": "b", "type": "string"}]
}
实例化这个record,a字段的值是27(编码为0×36),b字段的值是“foo”(编码为06 66 6f 6f),那么这个record编码结果是:
36 06 66 6f 6f
3.3.enums
一个enum被编码为一个int,比如,考虑这个enum。
{"type": "enum", "name": "Foo", "symbols": ["A", "B", "C", "D"] }
这将被编码为一个取值范围为[0,3]的int,0表示“A”,3表示“D”。
3.4.arrays
arrays编码为block序列,每个block包含一个long的count值,紧跟着的是array items,一个block的count为0表示该block是array的结尾。
3.5.maps
mapss编码为block序列,每个block包含一个long的count值,紧跟着的是key/value对,一个block的count为0表示该block是map的结尾。
3.6.union
union编码以一个long值开始,表示后边的数据是union中的哪种数据类型。
3.7.fixed
编码为指定数目的字节。
4.rpc通信实现
Avro的RPC实现不需要定义服务接口,但需要从.avpr文件中解析协议,协议中定义了消息结构和消息服务。message.avpr中定义了一个类型叫message,定义了一个服务叫sendMessage。
工具类Utils.java:
packageavro;
importjava.io.File;
importjava.io.IOException;
importjava.net.URL;
importorg.apache.avro.Protocol;
publicclassUtils{
publicstaticProtocolgetProtocol(){
Protocolprotocol=null;
try{
URLurl=Utils.class.getClassLoader().getResource("message.avpr");
protocol=Protocol.parse(newFile(url.getPath()));
}catch(IOExceptione){
e.printStackTrace();
}
returnprotocol;
}
}
服务端实现Server.java:
packageavro;
importorg.apache.avro.Protocol;
importorg.apache.avro.Protocol.Message;
importorg.apache.avro.generic.GenericRecord;
importorg.apache.avro.ipc.HttpServer;
importorg.apache.avro.ipc.generic.GenericResponder;
publicclassServerextendsGenericResponder
{
privateProtocolprotocol=
null;
privateintport;
publicServer(Protocolprotocol,
intport){
super(protocol);
this.protocol=protocol;
this.port=port;
}
publicObjectrespond(Messagemessage,Objectrequest)throwsException{
GenericRecordreq=(GenericRecord)request;
GenericRecordmsg=(GenericRecord)(req.get("message"));
// process the request
…
returnmsg;
}
publicvoidrun(){
try{
HttpServerserver=newHttpServer(this,port);
server.start();
}catch(Exceptione){
e.printStackTrace();
}
}
publicstaticvoidmain(String[]args){
if(args.length!=1){
System.out.println("Usage:
Server port");
System.exit(0);
}
intport=Integer.parseInt(args[0]);
newServer(Utils.getProtocol(),port).run();
}
}
客户端实现Client.java:
packageavro;
importjava.net.URL;
importjava.nio.ByteBuffer;
importjava.util.Arrays;
importorg.apache.avro.util.Utf8;
importorg.apache.avro.Protocol;
importorg.apache.avro.generic.GenericData;
importorg.apache.avro.generic.GenericRecord;
importorg.apache.avro.ipc.HttpTransceiver;
importorg.apache.avro.ipc.Transceiver;
importorg.apache.avro.ipc.generic.GenericRequestor;
publicclassClient{
privateProtocolprotocol=
null;
privateStringhost=null;
privateintport=0;
privateintsize=0;
privateintcount=0;
publicClient(Protocolprotocol,
Stringhost,intport,intsize,intcount){
this.protocol=protocol;
this.host=host;
this.port=port;
this.size=size;
this.count=count;
}
publiclongsendMessage()throwsException{
GenericRecordrequestData=newGenericData.Record(
protocol.getType("message"));
// initiate the request data
…
GenericRecordrequest=newGenericData.Record(protocol.getMessages()
.get("sendMessage").getRequest());
request.put("message",requestData);
Transceivert=newHttpTransceiver(newURL("http://"+host+":"
+port));
GenericRequestorrequestor=newGenericRequestor(protocol,t);
longstart=System.currentTimeMillis();
for(inti=0;i
requestor.request("sendMessage",request);
}
longend=System.currentTimeMillis();
System.out.println(end-start);
returnend-start;
}
publiclongrun(){
longres=0;
try{
res=sendMessage();
}catch(Exceptione){
e.printStackTrace();
}
returnres;
}
publicstaticvoidmain(String[]args)throwsException{
if(args.length!=4){
System.out.println("Usage:
Client host port dataSize count");
System.exit(0);
}
Stringhost=args[0];
intport=Integer.parseInt(args[1]);
intsize=Integer.parseInt(args[2]);
intcount=Integer.parseInt(args[3]);
newClient(Utils.getProtocol(),host,port,size,count).run();
}
}
5.参考资料