文章出處

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/

http://www.cnblogs.com/brucewoo/tag/Thrift/


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()