thrift的基本構架:

上圖源自:http://jnb.ociweb.com/jnb/jnbJun2009.html
底層Underlying I/O以上的部分,都是由thrift編譯器生成的代碼,其中:
Your Code 這是根據thrift文件中定義的dto及service接口方法
FooService.Client及FooService.Processer是thrift生成的用於客戶端及服務端的標准代碼
Foo.read/write 參數對象及結果對象在傳輸時,最終需要在client、server間進行重寫,紅色框指的就是這個
TProtocal 指傳輸的內容是啥?(二進制?Json ? )由於TProtocal是一個抽象類,因此最終調用時,如果想從BinaryProtocal換成JsonProtocal,這部分代碼也不用重新生成
TTransport 指用什么方式傳輸?(Scoket? Memory?File?)同樣,TTransport是抽象類,運行時由具體子類決定運輸方式
最底層的Underlying I/O則是依賴於各種語言的實現,負責底層的網絡通訊,thrift最初是由c++寫的,理論上講,c++上的性能應該最好。
以上一章的demo為例如,QueryParameter及DemoService的類圖如下:

點擊圖片可以查看大圖,從類圖上看,大量使用了內部類(inner class),對於dto對象,內部類基本上分為_Fileds, Schema,SchemaFactory 三類,
_Fields 是一個枚舉,羅列了dto的各種屬性成員,
Schema 封裝了write/read方法
SchemaFactory 是一個工廠,用於創建Schema實例

服務接口的類圖,就有點復雜了,密密麻麻象蜘蛛網,除了剛才的三大類外,DemoService的Inner Class中還有Client、Processor等類,大家有興趣可以慢慢看。
TProtocal : 傳輸的內容(即:What? )

從類圖上看,支持 壓縮格式、二進制格式、Json格式 等。
TTransport : 傳輸的方式(即:How? )

Thrift支持的傳輸方式非常多,從類的命名就能大概看出一二。
TServer: Server的類圖如下

基本上分為二大類:一類是同步阻塞的Server,一類是非阻塞模式的Server,其中THsHaServer是一個Half-Sync/Half-Async 半同步,半異步的server
meta_data 元數據

類圖中的xxxMetaData,基本對應了 列表、K-V映射、(無重復元素)集合、結構(即:類)、枚舉以及字段的元數據信息。
Schema :對不同類型的TProtocal的讀寫操作,在這里抽象出來。

Variable-Length Quantity VLQ 變長編碼:
Thirft采用TCompactProtocol序列化時之所以高效,跟VLQ變長編碼有很大關系,直接借下面這張圖來說吧:

整數106903,在java中我們知道int占用4個bytes,也就是32bit,高位字節如果不滿,用0填充(最高位符號位除外), 這樣的話,很多用0填充的高位字節位置其實是浪費的,VLQ的基本思路是將2進制每7位分組,這樣106903的2進制就可以分成3組,然后每1組的最高位設為1或0,如果為1,表示相鄰的下一個字節還有內容,要繼續讀取,如果該位置為0,則表示結束了。
這樣的話,106903最終只需要3個字節就可以存儲了,節省了1個字節。
上述這一堆概念在運行時,是如何串起來的呢?
可以從TServer的部分源碼中略知一二:
public abstract class TServer {
public static class Args extends AbstractServerArgs<Args> {
public Args(TServerTransport transport) {
super(transport);
}
}
public static abstract class AbstractServerArgs<T extends AbstractServerArgs<T>> {
final TServerTransport serverTransport;
TProcessorFactory processorFactory;
TTransportFactory inputTransportFactory = new TTransportFactory();
TTransportFactory outputTransportFactory = new TTransportFactory();
TProtocolFactory inputProtocolFactory = new TBinaryProtocol.Factory();
TProtocolFactory outputProtocolFactory = new TBinaryProtocol.Factory();
public AbstractServerArgs(TServerTransport transport) {
serverTransport = transport;
}
...
/**
* Core processor
*/
protected TProcessorFactory processorFactory_;
/**
* Server transport
*/
protected TServerTransport serverTransport_;
/**
* Input Transport Factory
*/
protected TTransportFactory inputTransportFactory_;
/**
* Output Transport Factory
*/
protected TTransportFactory outputTransportFactory_;
/**
* Input Protocol Factory
*/
protected TProtocolFactory inputProtocolFactory_;
/**
* Output Protocol Factory
*/
protected TProtocolFactory outputProtocolFactory_;
private boolean isServing;
protected TServerEventHandler eventHandler_;
// Flag for stopping the server
// Please see THRIFT-1795 for the usage of this flag
protected volatile boolean stopped_ = false;
protected TServer(AbstractServerArgs args) {
processorFactory_ = args.processorFactory;
serverTransport_ = args.serverTransport;
inputTransportFactory_ = args.inputTransportFactory;
outputTransportFactory_ = args.outputTransportFactory;
inputProtocolFactory_ = args.inputProtocolFactory;
outputProtocolFactory_ = args.outputProtocolFactory;
}
從上述源碼可以看出,TServer 中包含有input/output二類TProtocol,即:體現了 數據進來和出去時傳輸的格式(Binary? Json?...),另外還有input與output的Transport,即:數據進來和出去的時候,如何傳輸?(Scoket? File?...),另外還有一個Processor,其子類是通過IDL(thrift定義文件)生成的,運行時必須傳遞進來具體的子類。
這樣,傳遞什么數據(what)?用什么方式傳輸(how)? 以及數據如何處理(process)?都有了
而且從源碼中,可以發現默認的input/output Protocol都是BinaryProtocol(14,15行)。
Server端啟動的時序圖如下:

Client調用的時序圖:

注:上面這二張序列圖均出自https://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/
有了這些整體的概念后,上一篇中的示例,如果我們想換成TCompactProtocal,Client與Server的代碼都得同步修改,這樣二邊才能一致:
Client端:
public class ThriftClient {
public static void main(String[] args) {
try {
TTransport transport;
transport = new TSocket("localhost", 9090);
transport.open();
//TProtocol protocol = new TBinaryProtocol(transport);
TProtocol protocol = new TCompactProtocol(transport);
DemoService.Client client = new DemoService.Client(protocol);
...
Server端:
public static void simple(DemoService.Processor processor) {
try {
TServerTransport serverTransport = new TServerSocket(9090);
Args args = new Args(serverTransport);
args.outputProtocolFactory(new TCompactProtocol.Factory());
args.inputProtocolFactory(new TCompactProtocol.Factory());
TServer server = new TSimpleServer(args.processor(processor));
//TServer server = new TSimpleServer(new Args(serverTransport).processor(processor));
System.out.println("Starting the simple server...");
server.serve();
} catch (Exception e) {
e.printStackTrace();
}
}
最后,對TCompactProtocol、TBinaryProtocol、TJSONProtocol這三種序列化協議簡單測試下效率:
@Test
public void testProtocol() throws Exception {
QueryParameter parameter = getQueryParameter();
//CompactProtocol測試
TSerializer serializerCompact = new TSerializer(new TCompactProtocol.Factory());
byte[] bytes1 = serializerCompact.serialize(parameter);
System.out.println("CompactProtocol序列后的byte數組長度:" + bytes1.length);
//BinaryProtocol測試
TSerializer serializerBinary = new TSerializer(new TBinaryProtocol.Factory());
byte[] bytes2 = serializerBinary.serialize(parameter);
System.out.println("BinaryProtocol序列后的byte數組長度:" + bytes2.length);
//JsonProtocol測試
TSerializer serializerJson = new TSerializer(new TJSONProtocol.Factory());
byte[] bytes3 = serializerJson.serialize(parameter);
System.out.println("JsonProtocol序列后的byte數組長度:" + bytes3.length);
System.out.println(serializerJson.toString(parameter));
System.out.println("-----------");
TDeserializer deserializerCompact = new TDeserializer(new TCompactProtocol.Factory());
QueryParameter query1 = new QueryParameter();
deserializerCompact.deserialize(query1, bytes1);
System.out.println("CompactProtocol反序列化結果:" + query1.equals(parameter));
TDeserializer deserializerBinary = new TDeserializer(new TBinaryProtocol.Factory());
QueryParameter query2 = new QueryParameter();
deserializerBinary.deserialize(query2, bytes2);
System.out.println("BinaryProtocol反序列化結果:" + query2.equals(parameter));
TDeserializer deserializerJSON = new TDeserializer(new TJSONProtocol.Factory());
QueryParameter query3 = new QueryParameter();
deserializerJSON.deserialize(query3, bytes3);
System.out.println("JSONProtocol反序列化結果:" + query2.equals(parameter));
}
private QueryParameter getQueryParameter(){
QueryParameter query = new QueryParameter();
short start = 1;
short end = 5;
query.setAgeStart(start);
query.setAgeEnd(end);
return query;
}
輸出結果:
CompactProtocol序列后的byte數組長度:5
BinaryProtocol序列后的byte數組長度:11
JsonProtocol序列后的byte數組長度:29
{"1":{"i16":1},"2":{"i16":5}}
-----------
CompactProtocol反序列化結果:true
BinaryProtocol反序列化結果:true
JSONProtocol反序列化結果:true
TCompactProtocol優勢明顯,序列后的bytes長度只有JSON的1/5左右,可以大幅減少網絡傳輸量。
參考文章:
http://dongxicheng.org/search-engine/thrift-rpc/
http://blog.chinaunix.net/uid-20357359-id-2876170.html
http://jnb.ociweb.com/jnb/jnbJun2009.html
https://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/
