跨平臺通訊中間件thrift學習【Java版本】(轉)

轉自:http://neoremind.com/2012/03/%E8%B7%A8%E5%B9%B3%E5%8F%B0%E9%80%9A%E4%BF%A1%E4%B8%AD%E9%97%B4%E4%BB%B6thrift%E5%AD%A6%E4%B9%A0%E3%80%90java%E7%89%88%E6%9C%AC%E3%80%91/java

 

1. What is thrift?

Thrift是一個跨語言的服務部署框架,最初由Facebook於2007年開發,2008年進入Apache開源項目。跨平臺通訊中thrift能夠做爲二進制的高性能的通信中間件,支持數據(對象)序列化和多種類型的RPC服務。
 

2. thrift爲咱們作了什麼?

首先咱們須要先了解下任何RPC的解決方案都包含以下幾層實現:
· 服務層(service):RPC接口定義與實現
· 協議層(protocol):RPC報文格式和數據編碼格式
· 傳輸層(transport):實現底層的通訊(如 socket)以及系統相關的功能(如事件循環、多線程)
 
Thrift經過一箇中間語言(IDL, 接口定義語言)來定義RPC的接口和數據類型,而後經過一個編譯器生成不一樣語言的代碼(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk等),並由生成的代碼負責RPC協議層和傳輸層的實現。咱們只須要去實現具體的接口實現就能夠了。
 
Thrift其實是實現了請求響應的C/S模式,經過代碼生成工具將接口定義文件生成服務器端代碼,從而實現服務端和客戶端跨語言的支持。用戶在Thirft描述文件中聲明本身的服務,這些服務通過編譯後會生成相應語言的代碼文件,而後用戶實現服務即可以了。
 

3. thrift基本概念

數據類型

     * Base Types:基本類型
     * Struct:結構體類型
     * Container:容器類型,即List、Set、Map
     * Exception:異常類型
     * Service: 定義對象的接口,和一系列方法
 

協議層類型

Thrift可讓你選擇客戶端與服務端之間傳輸通訊協議的類別,在傳輸協議上整體上劃分爲文本(text)和二進制(binary)傳輸協議, 爲節約帶寬,提供傳輸效率,通常狀況下
 
使用二進制類型的傳輸協議爲多數,但有時會仍是會使用基於文本類型的協議,這須要根據項目/產品中的實際需求:
    * TBinaryProtocol – 二進制編碼格式進行數據傳輸。
    * TCompactProtocol – 這種協議很是有效的,使用Variable-Length Quantity (VLQ) 編碼對數據進行壓縮。
    * TJSONProtocol – 使用JSON的數據編碼協議進行數據傳輸。
    * TSimpleJSONProtocol – 這種節約只提供JSON只寫的協議,適用於經過腳本語言解析
    * TDebugProtocol – 在開發的過程當中幫助開發人員調試用的,以文本的形式展示方便閱讀。
 

傳輸層類型

    * TSocket – 使用堵塞式I/O進行傳輸,也是最多見的模式。
    * THttpTransport – 採用Http傳輸協議進行數據傳輸
    * TFileTransport – 顧名思義按照文件的方式進程傳輸,雖然這種方式不提供Java的實現,可是實現起來很是簡單。
    * TZlibTransport – 使用執行zlib壓縮,不提供Java的實現。
 
    下面幾個類主要是對上面幾個類地裝飾(採用了裝飾模式),以提升傳輸效率。
    TBufferedTransport – 對某個Transport對象操做的數據進行buffer,即從buffer中讀取數據進行傳輸,或者將數據直接寫入buffer
    TFramedTransport – 以frame爲單位進行傳輸,非阻塞式服務中使用。同TBufferedTransport相似,也會對相關數據進行buffer,同時,它支持定長數據發送和接收。
    TMemoryBuffer – 從一個緩衝區中讀寫數據,使用內存I/O,就比如Java中的ByteArrayOutputStream實現。
 

服務端類型

    * TSimpleServer– 簡單的單線程服務模型,經常使用於測試
    * TThreadedServer – 多線程服務模型,使用阻塞式IO,每一個請求建立一個線程。
    * TThreadPoolServer – 線程池服務模型,使用標準的阻塞式IO,預先建立一組線程處理請求。
    * TNonblockingServer – 多線程服務模型,使用非阻塞式IO(需使用TFramedTransport數據傳輸方式)
 

4. Thrift 架構

Thrift 包含一個完整的堆棧結構用於構建客戶端和服務器端。下圖描繪了Thrift的總體架構。
 
 
如圖所示,圖中黃色部分是用戶實現的業務邏輯,褐色部分是根據 Thrift 定義的服務接口描述文件生成的客戶端和服務器端代碼框架,紅色部分是根據 Thrift 文件生成代碼實現數據的讀寫操做。紅色部分如下是 Thrift 的傳輸體系、協議以及底層 I/O 通訊,使用 Thrift 能夠很方便的定義一個服務而且選擇不一樣的傳輸協議和傳輸層而不用從新生成代碼。
 
Thrift 服務器包含用於綁定協議和傳輸層的基礎架構,它提供阻塞、非阻塞、單線程和多線程的模式運行在服務器上,能夠配合服務器 / 容器一塊兒運行,能夠和現有的 J2EE 服務器 /Web 容器無縫的結合。
 

5. 安裝

http://thrift.apache.org/ 官網下載,須要用ant編譯成lib,最終實際項目中只須要加入以下jar包:
* libthrift.jar
* slf4j-api-1.5.8.jar
* slf4j-log4j12-1.5.8.jar
* log4j-1.2.15.jar
 

6. 簡單實例

建立一個簡單的Helloworld。首先根據Thrift的語法規範編寫腳本文件Hello.thrift,代碼以下:
 
 namespace java service.demo 
 service Hello{ 
  string helloString(1:string para) 
  i32 helloInt(1:i32 para) 
  bool helloBoolean(1:bool para) 
  void helloVoid() 
  string helloNull() 
 }
 
其中定義了服務 Hello 的五個方法,每一個方法包含一個方法名,參數列表和返回類型。每一個參數包括參數序號,參數類型以及參數名。
 
使用thrift.exe -gen java Hello.thrift生成兩個gen-java文件夾,裏面就有咱們生成的服務器端接口Hello.java了。
 
建立 HelloServiceImpl.java 文件並實現 Hello.java 文件中的 Hello.Iface 接口,代碼以下:
 
 
 package service.demo; 
 import org.apache.thrift.TException; 
 public class HelloServiceImpl implements Hello.Iface { 
    @Override 
    public boolean helloBoolean(boolean para) throws TException { 
        return para; 
    } 
    @Override 
    public int helloInt(int para) throws TException { 
        try { 
            Thread.sleep(20000); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
        return para; 
    } 
    @Override 
    public String helloNull() throws TException { 
        return null; 
    } 
    @Override 
    public String helloString(String para) throws TException { 
        return para; 
    } 
    @Override 
    public void helloVoid() throws TException { 
        System.out.println("Hello World"); 
    } 
 }
 
建立服務器端實現代碼,將 HelloServiceImpl 做爲具體的處理器傳遞給 Thrift 服務器,代碼以下:
 

 

 
 package service.server; 
 import org.apache.thrift.TProcessor; 
 import org.apache.thrift.protocol.TBinaryProtocol; 
 import org.apache.thrift.protocol.TBinaryProtocol.Factory; 
 import org.apache.thrift.server.TServer; 
 import org.apache.thrift.server.TThreadPoolServer; 
 import org.apache.thrift.transport.TServerSocket; 
 import org.apache.thrift.transport.TTransportException; 
 import service.demo.Hello; 
 import service.demo.HelloServiceImpl; 
 
 public class HelloServiceServer { 
    /** 
     * 啓動 Thrift 服務器
     * @param args 
     */ 
    public static void main(String[] args) { 
        try { 
            // 設置服務端口爲 7911 
            TServerSocket serverTransport = new TServerSocket(7911); 
            // 設置協議工廠爲 TBinaryProtocol.Factory 
            Factory proFactory = new TBinaryProtocol.Factory(); 
            // 關聯處理器與 Hello 服務的實現
            TProcessor processor = new Hello.Processor(new HelloServiceImpl()); 
            TServer server = new TThreadPoolServer(processor, serverTransport, 
                    proFactory); 
            System.out.println("Start server on port 7911..."); 
            server.serve(); 
        } catch (TTransportException e) { 
            e.printStackTrace(); 
        } 
    } 
 }
建立客戶端實現代碼,調用 Hello.client 訪問服務端的邏輯實現,代碼以下:
 
package service.client; 
 import org.apache.thrift.TException; 
 import org.apache.thrift.protocol.TBinaryProtocol; 
 import org.apache.thrift.protocol.TProtocol; 
 import org.apache.thrift.transport.TSocket; 
 import org.apache.thrift.transport.TTransport; 
 import org.apache.thrift.transport.TTransportException; 
 import service.demo.Hello; 
 
 public class HelloServiceClient { 
 /** 
     * 調用 Hello 服務
     * @param args 
     */ 
    public static void main(String[] args) { 
        try { 
            // 設置調用的服務地址爲本地,端口爲 7911 
            TTransport transport = new TSocket("localhost", 7911); 
            transport.open(); 
            // 設置傳輸協議爲 TBinaryProtocol 
            TProtocol protocol = new TBinaryProtocol(transport); 
            Hello.Client client = new Hello.Client(protocol); 
            // 調用服務的 helloVoid 方法
            client.helloVoid(); 
            transport.close(); 
        } catch (TTransportException e) { 
            e.printStackTrace(); 
        } catch (TException e) { 
            e.printStackTrace(); 
        } 
    } 
 }
 
代碼編寫完後運行服務器,再啓動客戶端調用服務 Hello 的方法 helloVoid,在服務器端的控制檯窗口輸出「Hello World」。
相關文章
相關標籤/搜索