Apache Thrift - 可伸縮的跨語(yǔ)言服務(wù)開(kāi)發(fā)框架(轉(zhuǎn))

轉(zhuǎn)自:http://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/#ibm-pcon

前言:

目前流行的服務(wù)調(diào)用方式有很多種,例如基于 SOAP 消息格式的 Web Service,基于 JSON 消息格式的 RESTful 服務(wù)等。其中所用到的數(shù)據(jù)傳輸方式包括 XML,JSON 等,然而 XML 相對(duì)體積太大,傳輸效率低,JSON 體積較小,新穎,但還不夠完善。本文將介紹由 Facebook 開(kāi)發(fā)的遠(yuǎn)程服務(wù)調(diào)用框架 Apache Thrift,它采用接口描述語(yǔ)言定義并創(chuàng)建服務(wù),支持可擴(kuò)展的跨語(yǔ)言服務(wù)開(kāi)發(fā),所包含的代碼生成引擎可以在多種語(yǔ)言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk 等創(chuàng)建高效的、無(wú)縫的服務(wù),其傳輸數(shù)據(jù)采用二進(jìn)制格式,相對(duì) XML 和 JSON 體積更小,對(duì)于高并發(fā)、大數(shù)據(jù)量和多語(yǔ)言的環(huán)境更有優(yōu)勢(shì)。本文將詳細(xì)介紹 Thrift 的使用,并且提供豐富的實(shí)例代碼加以解釋說(shuō)明,幫助使用者快速構(gòu)建服務(wù)。

一個(gè)簡(jiǎn)單的 Thrift 實(shí)例

本文首先介紹一個(gè)簡(jiǎn)單的 Thrift 實(shí)現(xiàn)實(shí)例,使讀者能夠快速直觀地了解什么是 Thrift 以及如何使用 Thrift 構(gòu)建服務(wù)。

創(chuàng)建一個(gè)簡(jiǎn)單的服務(wù) Hello。首先根據(jù) Thrift 的語(yǔ)法規(guī)范編寫(xiě)腳本文件 Hello.thrift,代碼如下:

清單 1. 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() 
}

其中定義了服務(wù) Hello 的五個(gè)方法,每個(gè)方法包含一個(gè)方法名,參數(shù)列表和返回類型。每個(gè)參數(shù)包括參數(shù)序號(hào),參數(shù)類型以及參數(shù)名。 Thrift 是對(duì) IDL(Interface Definition Language) 描述性語(yǔ)言的一種具體實(shí)現(xiàn)。因此,以上的服務(wù)描述文件使用 IDL 語(yǔ)法編寫(xiě)。使用 Thrift 工具編譯 Hello.thrift,就會(huì)生成相應(yīng)的 Hello.java 文件。該文件包含了在 Hello.thrift 文件中描述的服務(wù) Hello 的接口定義,即 Hello.Iface 接口,以及服務(wù)調(diào)用的底層通信細(xì)節(jié),包括客戶端的調(diào)用邏輯 Hello.Client 以及服務(wù)器端的處理邏輯 Hello.Processor,用于構(gòu)建客戶端和服務(wù)器端的功能。

創(chuàng)建 HelloServiceImpl.java 文件并實(shí)現(xiàn) Hello.java 文件中的 Hello.Iface 接口,代碼如下:

清單 2. HelloServiceImpl.java
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"); 
  } 
}

創(chuàng)建服務(wù)器端實(shí)現(xiàn)代碼,將 HelloServiceImpl 作為具體的處理器傳遞給 Thrift 服務(wù)器,代碼如下:

清單 3. HelloServiceServer.java
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 { 
   /** 
    * 啟動(dòng) Thrift 服務(wù)器
    * @param args 
    */ 
   public static void main(String[] args) { 
       try { 
           // 設(shè)置服務(wù)端口為 7911 
           TServerSocket serverTransport = new TServerSocket(7911); 
           // 設(shè)置協(xié)議工廠為 TBinaryProtocol.Factory 
           Factory proFactory = new TBinaryProtocol.Factory(); 
           // 關(guān)聯(lián)處理器與 Hello 服務(wù)的實(shí)現(xiàn)
           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(); 
       } 
   } 
}

創(chuàng)建客戶端實(shí)現(xiàn)代碼,調(diào)用 Hello.client 訪問(wèn)服務(wù)端的邏輯實(shí)現(xiàn),代碼如下:

清單 4. HelloServiceClient.java
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 { 
/** 
    * 調(diào)用 Hello 服務(wù)
    * @param args 
    */ 
   public static void main(String[] args) { 
       try { 
           // 設(shè)置調(diào)用的服務(wù)地址為本地,端口為 7911 
           TTransport transport = new TSocket("localhost", 7911); 
           transport.open(); 
           // 設(shè)置傳輸協(xié)議為 TBinaryProtocol 
           TProtocol protocol = new TBinaryProtocol(transport); 
           Hello.Client client = new Hello.Client(protocol); 
           // 調(diào)用服務(wù)的 helloVoid 方法
           client.helloVoid(); 
           transport.close(); 
       } catch (TTransportException e) { 
           e.printStackTrace(); 
       } catch (TException e) { 
           e.printStackTrace(); 
       } 
   } 
}

代碼編寫(xiě)完后運(yùn)行服務(wù)器,再啟動(dòng)客戶端調(diào)用服務(wù) Hello 的方法 helloVoid,在服務(wù)器端的控制臺(tái)窗口輸出“Hello World”(helloVoid 方法實(shí)現(xiàn)在控制臺(tái)打印字符串,沒(méi)有返回值,所以客戶端調(diào)用方法后沒(méi)有返回值輸出,讀者可以自己嘗試其他有返回值方法的調(diào)用,其結(jié)果可以打印在客戶端的控制臺(tái)窗口 )。

Thrift 架構(gòu)

Thrift 包含一個(gè)完整的堆棧結(jié)構(gòu)用于構(gòu)建客戶端和服務(wù)器端。下圖描繪了 Thrift 的整體架構(gòu)。

圖 1. 架構(gòu)圖
圖 1. 架構(gòu)圖

如圖所示,圖中黃色部分是用戶實(shí)現(xiàn)的業(yè)務(wù)邏輯,褐色部分是根據(jù) Thrift 定義的服務(wù)接口描述文件生成的客戶端和服務(wù)器端代碼框架,紅色部分是根據(jù) Thrift 文件生成代碼實(shí)現(xiàn)數(shù)據(jù)的讀寫(xiě)操作。紅色部分以下是 Thrift 的傳輸體系、協(xié)議以及底層 I/O 通信,使用 Thrift 可以很方便的定義一個(gè)服務(wù)并且選擇不同的傳輸協(xié)議和傳輸層而不用重新生成代碼。

Thrift 服務(wù)器包含用于綁定協(xié)議和傳輸層的基礎(chǔ)架構(gòu),它提供阻塞、非阻塞、單線程和多線程的模式運(yùn)行在服務(wù)器上,可以配合服務(wù)器 / 容器一起運(yùn)行,可以和現(xiàn)有的 J2EE 服務(wù)器 /Web 容器無(wú)縫的結(jié)合。

服務(wù)端和客戶端具體的調(diào)用流程如下:

圖 2. Server 端啟動(dòng)、服務(wù)時(shí)序圖(查看大圖
圖 2. Server 端啟動(dòng)、服務(wù)時(shí)序圖

該圖所示是 HelloServiceServer 啟動(dòng)的過(guò)程以及服務(wù)被客戶端調(diào)用時(shí),服務(wù)器的響應(yīng)過(guò)程。從圖中我們可以看到,程序調(diào)用了 TThreadPoolServer 的 serve 方法后,server 進(jìn)入阻塞監(jiān)聽(tīng)狀態(tài),其阻塞在 TServerSocket 的 accept 方法上。當(dāng)接收到來(lái)自客戶端的消息后,服務(wù)器發(fā)起一個(gè)新線程處理這個(gè)消息請(qǐng)求,原線程再次進(jìn)入阻塞狀態(tài)。在新線程中,服務(wù)器通過(guò) TBinaryProtocol 協(xié)議讀取消息內(nèi)容,調(diào)用 HelloServiceImpl 的 helloVoid 方法,并將結(jié)果寫(xiě)入 helloVoid_result 中傳回客戶端。

圖 3. Client 端調(diào)用服務(wù)時(shí)序圖(查看大圖
圖 3. Client 端調(diào)用服務(wù)時(shí)序圖

該圖所示是 HelloServiceClient 調(diào)用服務(wù)的過(guò)程以及接收到服務(wù)器端的返回值后處理結(jié)果的過(guò)程。從圖中我們可以看到,程序調(diào)用了 Hello.Client 的 helloVoid 方法,在 helloVoid 方法中,通過(guò) send_helloVoid 方法發(fā)送對(duì)服務(wù)的調(diào)用請(qǐng)求,通過(guò) recv_helloVoid 方法接收服務(wù)處理請(qǐng)求后返回的結(jié)果。

數(shù)據(jù)類型

Thrift 腳本可定義的數(shù)據(jù)類型包括以下幾種類型:

  • 基本類型:

  • bool:布爾值,true 或 false,對(duì)應(yīng) Java 的 boolean

  • byte:8 位有符號(hào)整數(shù),對(duì)應(yīng) Java 的 byte

  • i16:16 位有符號(hào)整數(shù),對(duì)應(yīng) Java 的 short

  • i32:32 位有符號(hào)整數(shù),對(duì)應(yīng) Java 的 int

  • i64:64 位有符號(hào)整數(shù),對(duì)應(yīng) Java 的 long

  • double:64 位浮點(diǎn)數(shù),對(duì)應(yīng) Java 的 double

  • string:未知編碼文本或二進(jìn)制字符串,對(duì)應(yīng) Java 的 String

  • 結(jié)構(gòu)體類型:

    • struct:定義公共的對(duì)象,類似于 C 語(yǔ)言中的結(jié)構(gòu)體定義,在 Java 中是一個(gè) JavaBean
  • 容器類型:

    • list:對(duì)應(yīng) Java 的 ArrayList
    • set:對(duì)應(yīng) Java 的 HashSet
    • map:對(duì)應(yīng) Java 的 HashMap
  • 異常類型:

    • exception:對(duì)應(yīng) Java 的 Exception
  • 服務(wù)類型:

    • service:對(duì)應(yīng)服務(wù)的類

協(xié)議

Thrift 可以讓用戶選擇客戶端與服務(wù)端之間傳輸通信協(xié)議的類別,在傳輸協(xié)議上總體劃分為文本 (text) 和二進(jìn)制 (binary) 傳輸協(xié)議,為節(jié)約帶寬,提高傳輸效率,一般情況下使用二進(jìn)制類型的傳輸協(xié)議為多數(shù),有時(shí)還會(huì)使用基于文本類型的協(xié)議,這需要根據(jù)項(xiàng)目 / 產(chǎn)品中的實(shí)際需求。常用協(xié)議有以下幾種:

  • TBinaryProtocol —— 二進(jìn)制編碼格式進(jìn)行數(shù)據(jù)傳輸

    使用方法如清單 3 和清單 4 所示。

  • TCompactProtocol —— 高效率的、密集的二進(jìn)制編碼格式進(jìn)行數(shù)據(jù)傳輸

    構(gòu)建 TCompactProtocol 協(xié)議的服務(wù)器和客戶端只需替換清單 3 和清單 4 中 TBinaryProtocol 協(xié)議部分即可,替換成如下代碼:

清單 5. 使用 TCompactProtocol 協(xié)議構(gòu)建的 HelloServiceServer.java
TCompactProtocol.Factory proFactory = new TCompactProtocol.Factory();
清單 6. 使用 TCompactProtocol 協(xié)議的 HelloServiceClient.java
TCompactProtocol protocol = new TCompactProtocol(transport);
  • TJSONProtocol —— 使用 JSON 的數(shù)據(jù)編碼協(xié)議進(jìn)行數(shù)據(jù)傳輸

    構(gòu)建 TJSONProtocol 協(xié)議的服務(wù)器和客戶端只需替換清單 3 和清單 4 中 TBinaryProtocol 協(xié)議部分即可,替換成如下代碼:

清單 7. 使用 TJSONProtocol 協(xié)議構(gòu)建的 HelloServiceServer.java
  TJSONProtocol.Factory proFactory = new TJSONProtocol.Factory();
清單 8. 使用 TJSONProtocol 協(xié)議的 HelloServiceClient.java
TJSONProtocol protocol = new TJSONProtocol(transport);
  • TSimpleJSONProtocol —— 只提供 JSON 只寫(xiě)的協(xié)議,適用于通過(guò)腳本語(yǔ)言解析

傳輸層

常用的傳輸層有以下幾種:

  • TSocket —— 使用阻塞式 I/O 進(jìn)行傳輸,是最常見(jiàn)的模式

    使用方法如清單 4 所示。

  • TFramedTransport —— 使用非阻塞方式,按塊的大小進(jìn)行傳輸,類似于 Java 中的 NIO

    若使用 TFramedTransport 傳輸層,其服務(wù)器必須修改為非阻塞的服務(wù)類型,客戶端只需替換清單 4 中 TTransport 部分,代碼如下,清單 9 中 TNonblockingServerTransport 類是構(gòu)建非阻塞 socket 的抽象類,TNonblockingServerSocket 類繼承 TNonblockingServerTransport

    清單 9. 使用 TFramedTransport 傳輸層構(gòu)建的 HelloServiceServer.java
TNonblockingServerTransport serverTransport; 
serverTransport = new TNonblockingServerSocket(10005); 
Hello.Processor processor = new Hello.Processor(new HelloServiceImpl()); 
TServer server = new TNonblockingServer(processor, serverTransport); 
System.out.println("Start server on port 10005 ..."); 
server.serve();
清單 10. 使用 TFramedTransport 傳輸層的 HelloServiceClient.java
TTransport transport = new TFramedTransport(new TSocket("localhost", 10005));
  • TNonblockingTransport —— 使用非阻塞方式,用于構(gòu)建異步客戶端

    使用方法請(qǐng)參考 Thrift 異步客戶端構(gòu)建

服務(wù)端類型

常見(jiàn)的服務(wù)端類型有以下幾種:

  • TSimpleServer —— 單線程服務(wù)器端使用標(biāo)準(zhǔn)的阻塞式 I/O

    代碼如下:

    清單 11. 使用 TSimpleServer 服務(wù)端構(gòu)建的 HelloServiceServer.java
TServerSocket serverTransport = new TServerSocket(7911); 
TProcessor processor = new Hello.Processor(new HelloServiceImpl()); 
TServer server = new TSimpleServer(processor, serverTransport); 
System.out.println("Start server on port 7911..."); 
server.serve();
客戶端的構(gòu)建方式可參考清單 4。
  • TThreadPoolServer —— 多線程服務(wù)器端使用標(biāo)準(zhǔn)的阻塞式 I/O

    使用方法如清單 3 所示。

  • TNonblockingServer —— 多線程服務(wù)器端使用非阻塞式 I/O

    使用方法請(qǐng)參考 Thrift 異步客戶端構(gòu)建

Thrift 異步客戶端構(gòu)建

Thrift 提供非阻塞的調(diào)用方式,可構(gòu)建異步客戶端。在這種方式中,Thrift 提供了新的類 TAsyncClientManager 用于管理客戶端的請(qǐng)求,在一個(gè)線程上追蹤請(qǐng)求和響應(yīng),同時(shí)通過(guò)接口 AsyncClient 傳遞標(biāo)準(zhǔn)的參數(shù)和 callback 對(duì)象,服務(wù)調(diào)用完成后,callback 提供了處理調(diào)用結(jié)果和異常的方法。

首先我們看 callback 的實(shí)現(xiàn):

清單 12.CallBack 的實(shí)現(xiàn):MethodCallback.java
package service.callback; 
import org.apache.thrift.async.AsyncMethodCallback; 
 
public class MethodCallback implements AsyncMethodCallback { 
   Object response = null; 
 
   public Object getResult() { 
       // 返回結(jié)果值
       return this.response; 
   } 
 
   // 處理服務(wù)返回的結(jié)果值
   @Override 
   public void onComplete(Object response) { 
       this.response = response; 
   } 
   // 處理調(diào)用服務(wù)過(guò)程中出現(xiàn)的異常
   @Override 
   public void onError(Throwable throwable) { 
 
   } 
}

如代碼所示,onComplete 方法接收服務(wù)處理后的結(jié)果,此處我們將結(jié)果 response 直接賦值給 callback 的私有屬性 response。onError 方法接收服務(wù)處理過(guò)程中拋出的異常,此處未對(duì)異常進(jìn)行處理。

創(chuàng)建非阻塞服務(wù)器端實(shí)現(xiàn)代碼,將 HelloServiceImpl 作為具體的處理器傳遞給異步 Thrift 服務(wù)器,代碼如下:

清單 13.HelloServiceAsyncServer.java
package service.server; 
import org.apache.thrift.server.TNonblockingServer; 
import org.apache.thrift.server.TServer; 
import org.apache.thrift.transport.TNonblockingServerSocket; 
import org.apache.thrift.transport.TNonblockingServerTransport; 
import org.apache.thrift.transport.TTransportException; 
import service.demo.Hello; 
import service.demo.HelloServiceImpl; 
 
public class HelloServiceAsyncServer { 
   /** 
    * 啟動(dòng) Thrift 異步服務(wù)器
    * @param args 
    */ 
   public static void main(String[] args) { 
       TNonblockingServerTransport serverTransport; 
       try { 
           serverTransport = new TNonblockingServerSocket(10005); 
           Hello.Processor processor = new Hello.Processor( 
                   new HelloServiceImpl()); 
           TServer server = new TNonblockingServer(processor, serverTransport); 
           System.out.println("Start server on port 10005 ..."); 
           server.serve(); 
       } catch (TTransportException e) { 
           e.printStackTrace(); 
       } 
   } 
}

HelloServiceAsyncServer 通過(guò) java.nio.channels.ServerSocketChannel 創(chuàng)建非阻塞的服務(wù)器端等待客戶端的連接。

創(chuàng)建異步客戶端實(shí)現(xiàn)代碼,調(diào)用 Hello.AsyncClient 訪問(wèn)服務(wù)端的邏輯實(shí)現(xiàn),將 MethodCallback 對(duì)象作為參數(shù)傳入調(diào)用方法中,代碼如下:

清單 14.HelloServiceAsyncClient.java
package service.client; 
import java.io.IOException; 
import org.apache.thrift.async.AsyncMethodCallback; 
import org.apache.thrift.async.TAsyncClientManager; 
import org.apache.thrift.protocol.TBinaryProtocol; 
import org.apache.thrift.protocol.TProtocolFactory; 
import org.apache.thrift.transport.TNonblockingSocket; 
import org.apache.thrift.transport.TNonblockingTransport; 
import service.callback.MethodCallback; 
import service.demo.Hello; 
 
public class HelloServiceAsyncClient { 
   /** 
    * 調(diào)用 Hello 服務(wù)
    * @param args 
    */ 
   public static void main(String[] args) throws Exception { 
       try { 
           TAsyncClientManager clientManager = new TAsyncClientManager(); 
           TNonblockingTransport transport = new TNonblockingSocket( 
                   "localhost", 10005); 
           TProtocolFactory protocol = new TBinaryProtocol.Factory(); 
           Hello.AsyncClient asyncClient = new Hello.AsyncClient(protocol, 
                   clientManager, transport); 
           System.out.println("Client calls ....."); 
           MethodCallback callBack = new MethodCallback(); 
           asyncClient.helloString("Hello World", callBack); 
           Object res = callBack.getResult(); 
           while (res == null) { 
               res = callBack.getResult(); 
           } 
           System.out.println(((Hello.AsyncClient.helloString_call) res) 
                   .getResult()); 
       } catch (IOException e) { 
           e.printStackTrace(); 
       } 
 } 
}

HelloServiceAsyncClient 通過(guò) java.nio.channels.Socketchannel 創(chuàng)建異步客戶端與服務(wù)器建立連接。在本文中異步客戶端通過(guò)以下的循環(huán)代碼實(shí)現(xiàn)了同步效果,讀者可去除這部分代碼后再運(yùn)行對(duì)比。

清單 15. 異步客戶端實(shí)現(xiàn)同步效果代碼段
Object res = callBack.getResult();
// 等待服務(wù)調(diào)用后的返回結(jié)果
while (res == null) {
   res = callBack.getResult();
}

通過(guò)與清單 9 和清單 10 的代碼比較,我們可以構(gòu)建一個(gè) TNonblockingServer 服務(wù)類型的服務(wù)端,在客戶端構(gòu)建一個(gè) TFramedTransport 傳輸層的同步客戶端和一個(gè) TNonblockingTransport 傳輸層的異步客戶端,那么一個(gè)服務(wù)就可以通過(guò)一個(gè) socket 端口提供兩種不同的調(diào)用方式。有興趣的讀者可以嘗試一下。

常見(jiàn)問(wèn)題

NULL 問(wèn)題

我們?cè)趯?duì)服務(wù)的某個(gè)方法調(diào)用時(shí),有時(shí)會(huì)出現(xiàn)該方法返回 null 值的情況,在 Thrift 中,直接調(diào)用一個(gè)返回 null 值的方法會(huì)拋出 TApplicationException 異常。在清單 2 中,HelloServiceImpl 里實(shí)現(xiàn)了 helloNull 方法,返回 null 值,我們?cè)?HelloServiceClient.java 中加入調(diào)用該方法的代碼,出現(xiàn)如下圖所示的異常:

圖 4. TApplicationException 異常
圖 4. TApplicationException 異常

為了處理返回 null 值情況,我們要捕獲該異常,并進(jìn)行相應(yīng)的處理,具體客戶端代碼實(shí)現(xiàn)如下:

清單 16. 處理服務(wù)返回值為 null 的代碼
package service.client; 
import org.apache.thrift.TApplicationException; 
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 { 
   /** 
    * 調(diào)用 Hello 服務(wù),并處理 null 值問(wèn)題
    * @param args 
    */ 
   public static void main(String[] args) { 
       try { 
           TTransport transport = new TSocket("localhost", 7911); 
           transport.open(); 
           TProtocol protocol = new TBinaryProtocol(transport); 
           Hello.Client client = new Hello.Client(protocol); 
           System.out.println(client.helloNull()); 
           transport.close(); 
       } catch (TTransportException e) { 
           e.printStackTrace(); 
       } catch (TException e) { 
           if (e instanceof TApplicationException 
                   && ((TApplicationException) e).getType() ==   
                                TApplicationException.MISSING_RESULT) { 
               System.out.println("The result of helloNull function is NULL"); 
           } 
       } 
   } 
}

調(diào)用 helloNull 方法后,會(huì)拋出 TApplicationException 異常,并且異常種類為 MISSING_RESULT,本段代碼顯示,捕獲該異常后,直接在控制臺(tái)打印“The result of helloNull function is NULL”信息。

安裝部署

Apache Thrift 的官方網(wǎng)站為:http://thrift.apache.org/tutorial/,具體安裝步驟如下:

  1. 下載 thrift 源文件(http://svn.apache.org/repos/asf/thrift/tags/thrift-0.6.1/
  2. 將 thrift 源文件導(dǎo)入 eclipse,進(jìn)入 /lib/java 目錄,使用 ant 編譯 build.xml 獲得 libthrift-0.6.1-snapshot.jar
  3. 將 libthrift-0.6.1-snapshot.jar、slf4j-api-1.5.8.jar、slf4j-log4j12-1.5.8.jar 和 log4j-1.2.14.jar 導(dǎo)入 eclipse 開(kāi)發(fā)環(huán)境
  4. 下載 thrift 編譯工具,該工具可將 thrift 腳本文件編譯成 java 文件,下載地址:http://apache.etoak.com//thrift/0.6.0/thrift-0.6.1.exe
  5. 創(chuàng)建 Hello.thrift 腳本文件,具體代碼如上一章節(jié)所述,進(jìn)入 thrift-0.6.1.exe 所在目錄,執(zhí)行命令"thrift-0.6.1.exe -gen java x:\Hello.thrift",在當(dāng)前運(yùn)行盤(pán)符下,可看見(jiàn) gen-java 目錄,進(jìn)入目錄可看到生成的 Java 代碼。更多 thrift 的命令內(nèi)容,請(qǐng)參考 thrift 自帶的 help 命令
  6. 編寫(xiě)服務(wù)端和客戶端代碼,完成 thrift 的安裝和部署

基于 Apache Thrift 框架生成的服務(wù)包括客戶端和服務(wù)器端,具體的部署模式如下所示:

圖 5. 部署圖
圖 5. 部署圖

從圖中我們可以看到,客戶端和服務(wù)器端部署時(shí),需要用到公共的 jar 包和 java 文件,如圖“Common file”區(qū)域,其中 Hello.java 由 Hello.thrift 編譯而來(lái)。在服務(wù)器端,服務(wù)必須實(shí)現(xiàn) Hello.Iface 接口,同時(shí)要包括服務(wù)器的啟動(dòng)代碼 HelloServiceServer.java。在客戶端,包括客戶端調(diào)用服務(wù)的代碼 HelloServiceClient.java。客戶端和服務(wù)器通過(guò) Hello.java 提供的 API 實(shí)現(xiàn)遠(yuǎn)程服務(wù)調(diào)用。

總結(jié)

本文介紹了 Apache Thrift 的安裝部署和架構(gòu),并通過(guò)大量實(shí)例介紹了在不同情況下如何使用 Apache Thrift 來(lái)構(gòu)建服務(wù),同時(shí)著重介紹了 Thrift 異步客戶端的構(gòu)建,希望能給讀者帶來(lái)一些幫助。

相關(guān)主題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,577評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,486評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,852評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,600評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,944評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 43,108評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,652評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,385評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,616評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,798評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,205評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,537評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,334評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,570評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容