http://www.alidata.org/archives/1307
java
Avro和Thrift都是跨語言,基於二進制的高性能的通信中間件. 它們都提供了數據序列化的功能和RPC服務. 整體功能上相似,可是哲學不同. Thrift出自Facebook用於後臺各個服務間的通信,Thrift的設計強調統一的編程接口的多語言通信框架. Avro出自Hadoop之父Doug Cutting, 在Thrift已經至關流行的狀況下Avro的推出,其目標不只是提供一套相似Thrift的通信中間件更是要創建一個新的,標準性的雲計算的數據交換和存儲的Protocol。 這個和Thrift的理念不一樣,Thrift認爲沒有一個完美的方案能夠解決全部問題,所以儘可能保持一個Neutral框架,插入不一樣的實現並互相交互。而Avro偏向實用,排斥多種方案帶來的 可能的混亂,主張創建一個統一的標準,並不介意採用特定的優化。Avro的創新之處在於融合了顯式,declarative的Schema和高效二進制的數據表達,強調數據的自我描述,克服了以往單純XML或二進制系統的缺陷。Avro對Schema動態加載功能,是Thrift編程接口所不具有的,符合了Hadoop上的Hive/Pig及NOSQL 等既屬於ad hoc,又追求性能的應用需求.apache
目前階段Thrift比Avro支持的語言更豐富.編程
Thrift: C++, C#, Cocoa, Erlang, Haskell, Java, Ocami, Perl, PHP, Python, Ruby, Smalltalk.服務器
Avro: C, C++, Java, Python, Ruby, PHP.網絡
從常見的數據類型的角度來講, Avro和Thrift很是接近,功能上並無什麼區別。數據結構
Avro | Thrift | ||
基本類型 | true or false多線程 |
||
N/A | 8-bit signed integer | ||
N/A | I16 | 16-bit signed integer | |
int | I32 | 32-bit signed integer | |
long | I64 | 64-bit signed integer | |
float | N/A | 32-bit floating point | |
double | double | 64-bit floating point | |
bytes | binary | Byte sequence | |
string | string | Character sequence | |
複雜類型 | |||
record | struct | 用戶自定義類型 | |
enum | enum | ||
array<T> | list<T> | ||
N/A | set<T> | ||
map<string,T> | map<T1,T2> | Avro map的key 必須是string併發 |
|
union | union | ||
fixed | N/A | 固定大小的byte array e.g. md5(16); |
|
RPC服務 | |||
protocol | service | RPC服務類型 | |
error | exception | RPC異常類型 | |
namespace | namespace | 域名 |
從開發者角度來講,Avro和Thrift也至關相似,app
1) 同一個服務分別用Avro和Thrift來描述框架
Avro.idl:
protocol SimpleService {
record Message {
string topic;
bytes content;
long createdTime;
string id;
string ipAddress;
map<string> props;
}
int publish(string context,array<Message> messages);
}
Thrift.idl:
struct Message {
1: string topic
2: binary content
3: i64 createdTime
4: string id
5: string ipAddress
6: map<string,string> props
}
service SimpleService {
i32 publish(1:string context,2:list<Message> messages);
}
2) Avro和Thrift都支持IDL代碼生成功能
java idl avro.idl idl.avro
java org.apache.avro.specific.SpecificCompiler idl.avro avro-gen
目標目錄生成Message.java和SimpleService.java
thrift -gen java thrift.idl
一樣的,目標目錄生成Message.java和SimpleService.java
3) 客戶端代碼
Avro client :
URL url = new URL ( 「http」, HOST, PORT, 「/」);
Transceiver trans = new HttpTransceiver(url);
SimpleService proxy=
= (SimpleService)SpecificRequestor.getClient(SimpleService.class, transceiver);
…
Thrift client :
TTransport transport = new TFramedTransport(new TSocket(HOST,PORT));
TProtocol protocol = new TCompactProtocol(transport);
transport.open();
SimpleService.Client client = new SimpleService.Client(protocol);
…
4) 服務器端 Avro和Thrift都生成接口須要實現:
Avro server:
public static class ServiceImpl implements SimpleService {
..
}
Responder responder = new SpecificResponder(SimpleService.class, new ServiceImpl());
Server server = new HttpServer(responder, PORT);
Thrift server:
public static class ServerImpl implements SimpleService.Iface {
..
}
TServerTransport serverTransport=new TServerSocket(PORT);
TServer server=new TSimpleServer(processor,serverTransport,new TFramedTransport.Factory(), new TCompactProtocol.Factory());
server.serve();
Avro和Thrift處理Schema方法大相徑庭。
Thrift是一個面向編程的系統, 徹底依賴於IDL->Binding Language的代碼生成。 Schema也「隱藏」在生成的代碼中了,徹底靜態。爲了讓系統識別處理一個新的數據源,必須走編輯IDL,代碼生成,編譯載入的流程。
與此對照,雖然Avro也支持基於IDL的Schema描述,但Avro內部Schema仍是顯式的,存在於JSON格式的文件當中,Avro能夠把IDL格式的Schema轉化成JSON格式的。
Avro支持2種方式。Avro-specific方式和Thrift的方式類似,依賴代碼生成產生特定的類,並內嵌JSON Schema. Avro-generic方式支持Schema的動態加載,用通用的結構(map)表明數據對象,不須要編譯加載直接就能夠處理新的數據源。
對於序列化Avro制定了一個協議,而Thrift的設計目標是一個框架,它沒有強制規定序列化的格式。
Avro規定一個標準的序列化的格式,即不管是文件存儲仍是網絡傳輸,數據的Schema(in JASON)都出如今數據的前面。數據自己並不包含任何Metadata(Tag). 在文件儲存的時候,schema出如今文件頭中。在網絡傳輸的時候Schema出如今初始的握手階段.這樣的好處一是使數據self describe,提升了數據的透明度和可操做性,二是減小了數據自己的信息量提升存儲效率,可謂一舉二得了
Avro的這種協議提供了不少優化的機會:
Avro的Schema容許定義數據的排序Order並在序列化的時候遵循這個順序。這樣話不須要反序列化就能夠直接對數據進行排序,在Hadoop裏很管用.
另一個Avro的特性是採用block鏈表結構,突破了用單一整型表示大小的限制。好比Array或Map由一系列Block組成,每一個Block包含計數器和對應的元素,計數器爲0標識結束。
Thrift提供了多種序列化的實現:
TCompactProtocol: 最高效的二進制序列化協議,但並非全部的綁定語言都支持。
TBinaryProtocol: 缺省簡單二進制序列化協議.
與Avro不一樣,Thrift的數據存儲的時候是每一個Field前面都是帶Tag的,這個Tag用於標識這個域的類型和順序ID(IDL中定義,用於Versioning)。在同一批數據裏面,這些Tag的信息是徹底相同的,當數據條數大的時候這顯然就浪費了。
Avro提供了
HttpServer : 缺省,基於Jetty內核的服務.
NettyServer: 新的基於Netty的服務.
Thrift提供了:
TThreadPolServer: 多線程服務
TNonBlockingServer: 單線程 non blocking的服務
THsHaServer: 多線程 non blocking的服務
測試環境:2臺4核 Intel Xeon 2.66GHz, 8G memory, Linux, 分別作客戶端,服務器。
Object definition:
record Message {
string topic;
bytes payload;
long createdTime;
string id;
string ipAddress;
map<string,string > props;
}
Actual instance:
msg.createdTime : System.nanoTime();
msg.ipAddress : 「127.0.0.1″;
msg.topic : 「pv」;
msg.payload : byte[100]
msg.id : UUID.randomUUID().toString();
msg.props : new HashMap<String,String>();
msg.props.put(「author」, 「tjerry」);
msg.props.put(「date」, new Date().toString());
msg.props.put(「status」, 「new」);
Serialization size
Avro的序列化產生的結果最小
Serialization speed
Thrift-binary由於序列化方式簡單反而看上去速度最快.
Deserialization speed
這裏 Thrift的速度很快, 因與它內部實現採用zero-copy的改進有關.不過在RPC綜合測試裏這一優點
彷佛並未體現出來.
序列化測試數據採集利用了http://code.google.com/p/thrift-protobuf-compare/所提供的框架,
原始輸出:
Starting
, Object create, Serialize, /w Same Object, Deserialize, and Check Media, and Check All, Total Time, Serialized Size
avro-generic , 8751.30500, 10938.00000, 1696.50000, 16825.00000, 16825.00000, 16825.00000, 27763.00000, 221
avro-specific , 8566.88000, 10534.50000, 1242.50000, 18157.00000, 18157.00000, 18157.00000, 28691.50000, 221
thrift-compact , 6784.61500, 11665.00000, 4214.00000, 1799.00000, 1799.00000, 1799.00000, 13464.00000, 227
thrift-binary , 6721.19500, 12386.50000, 4478.00000, 1692.00000, 1692.00000, 1692.00000, 14078.50000, 273
RPC測試用例:
客戶端向服務器發送一組固定長度的message,爲了可以同時測試序列和反序列,服務器收到後將原message返回給客戶端.
array<Message> publish(string context, array<Message> messages);
測試使用了Avro Netty Server和 Thrift HaHa Server由於他們都是基於異步IO的而且適用於高併發的環境。
結果
從這個測試來看,再未到達網絡瓶頸前,Avro Netty比Thrift HsHa服務提供了更高的吞吐率和更快的響應,另外 avro佔用的內存高些。
經過進一步實驗,發現不存在絕對的Avro和Thrift服務哪個更快,決定於給出的test case,或者說與程序的用法有關,好比當前測試用例是Batch模式,大量發送fine grained的對象(接近後臺tt,hadoop的用法),這個狀況下Avro有優點. 可是對於每次只傳一個對象的chatty客戶端,狀況就出現逆轉變成Thrift更高效了.還有當數據結構裏blob比例變大的狀況下,Avro和Thrift的差異也在減少.