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/
文章列表