Apache Thrift系列詳解(一)- 概述與入門


前言
Thrift是一個輕量級、跨語言的遠程服務調用框架,最初由Facebook開發,后面進入Apache開源項目。它通過自身的IDL中間語言, 並借助代碼生成引擎生成各種主流語言的RPC服務端/客戶端模板代碼。

Thrift支持多種不同的編程語言,包括C++、Java、Python、PHP、Ruby等,本系列主要講述基於Java語言的Thrift的配置方式和具體使用。

正文
Thrift的技術棧
Thrift對軟件棧的定義非常的清晰, 使得各個組件能夠松散的耦合, 針對不同的應用場景, 選擇不同是方式去搭建服務。

 

Thrift軟件棧分層從下向上分別為:傳輸層(Transport Layer)、協議層(Protocol Layer)、處理層(Processor Layer)和服務層(Server Layer)。

傳輸層(Transport Layer):傳輸層負責直接從網絡中讀取和寫入數據,它定義了具體的網絡傳輸協議;比如說TCP/IP傳輸等。

協議層(Protocol Layer):協議層定義了數據傳輸格式,負責網絡傳輸數據的序列化和反序列化;比如說JSON、XML、二進制數據等。

處理層(Processor Layer):處理層是由具體的IDL(接口描述語言)生成的,封裝了具體的底層網絡傳輸和序列化方式,並委托給用戶實現的Handler進行處理。

服務層(Server Layer):整合上述組件,提供具體的網絡線程/IO服務模型,形成最終的服務。

Thrift的特性
(一) 開發速度快
通過編寫RPC接口Thrift IDL文件,利用編譯生成器自動生成服務端骨架(Skeletons)和客戶端樁(Stubs)。從而省去開發者自定義和維護接口編解碼、消息傳輸、服務器多線程模型等基礎工作。

服務端:只需要按照服務骨架即接口,編寫好具體的業務處理程序(Handler)即實現類即可。
客戶端:只需要拷貝IDL定義好的客戶端樁和服務對象,然后就像調用本地對象的方法一樣調用遠端服務。
(二) 接口維護簡單
通過維護Thrift格式的IDL(接口描述語言)文件(注意寫好注釋),即可作為給Client使用的接口文檔使用,也自動生成接口代碼,始終保持代碼和文檔的一致性。且Thrift協議可靈活支持接口的可擴展性。

(三) 學習成本低
因為其來自Google Protobuf開發團隊,所以其IDL文件風格類似Google Protobuf,且更加易讀易懂;特別是RPC服務接口的風格就像寫一個面向對象的Class一樣簡單。

初學者只需參照:http://thrift.apache.org/,一個多小時就可以理解Thrift IDL文件的語法使用。

(四) 多語言/跨語言支持
Thrift支持C++、 Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk等多種語言,即可生成上述語言的服務器端和客戶端程序。

對於我們經常使用的Java、PHP、Python、C++支持良好,雖然對iOS環境的Objective-C(Cocoa)支持稍遜,但也完全滿足我們的使用要求。

(五) 穩定/廣泛使用
Thrift在很多開源項目中已經被驗證是穩定和高效的,例如Cassandra、Hadoop、HBase等;國外在Facebook中有廣泛使用,國內包括百度、美團小米、和餓了么等公司。

 

Thrift的數據類型
Thrift 腳本可定義的數據類型包括以下幾種類型:

基本類型:
  bool: 布爾值
  byte: 8位有符號整數
  i16: 16位有符號整數
  i32: 32位有符號整數
  i64: 64位有符號整數
  double: 64位浮點數
  string: UTF-8編碼的字符串
  binary: 二進制串
結構體類型:
  struct: 定義的結構體對象
容器類型:
  list: 有序元素列表
  set: 無序無重復元素集合
  map: 有序的key/value集合
異常類型:
  exception: 異常類型
服務類型:
  service: 具體對應服務的類
Thrift的協議
Thrift可以讓用戶選擇客戶端與服務端之間傳輸通信協議的類別,在傳輸協議上總體划分為文本(text)和二進制(binary)傳輸協議。為節約帶寬,提高傳輸效率,一般情況下使用二進制類型的傳輸協議為多數,有時還會使用基於文本類型的協議,這需要根據項目/產品中的實際需求。常用協議有以下幾種:

TBinaryProtocol:二進制編碼格式進行數據傳輸
TCompactProtocol:高效率的、密集的二進制編碼格式進行數據傳輸
TJSONProtocol: 使用JSON文本的數據編碼協議進行數據傳輸
TSimpleJSONProtocol:只提供JSON只寫的協議,適用於通過腳本語言解析
Thrift的傳輸層
常用的傳輸層有以下幾種:

TSocket:使用阻塞式I/O進行傳輸,是最常見的模式
TNonblockingTransport:使用非阻塞方式,用於構建異步客戶端
TFramedTransport:使用非阻塞方式,按塊的大小進行傳輸,類似於Java中的NIO
Thrift的服務端類型
TSimpleServer:單線程服務器端,使用標准的阻塞式I/O
TThreadPoolServer:多線程服務器端,使用標准的阻塞式I/O
TNonblockingServer:單線程服務器端,使用非阻塞式I/O
THsHaServer:半同步半異步服務器端,基於非阻塞式IO讀寫和多線程工作任務處理
TThreadedSelectorServer:多線程選擇器服務器端,對THsHaServer在異步IO模型上進行增強
Thrift入門示例
(一) 編寫Thrift IDL文件
a). 下載0.10.0的Thrift IDL編譯器,下載地址:http://thrift.apache.org/docs/install。 通過編譯生成器生成.java接口的類文件。

 

b). 下載Windows安裝環境的.exe文件,將thrift.exe的路徑加入環境變量中。在Idea上安裝Thrift編輯插件。

 

c). 編寫hello.thrift的IDL文件:

service HelloWorldService {
string say(1: string username)
}

d). 使用代碼生成工具生成代碼,執行以下命令:

thrift -gen java hello.thrift
1
e). 由於未指定代碼生成的目標目錄,生成的類文件默認存放在gen-java目錄下。這里生成一個HelloWorldService.java類文件,文件大小超過數千行,下面截取一部分核心代碼。

public class HelloWorldService {
public interface Iface {
public String say(String username) throws org.apache.thrift.TException;
}

public interface AsyncIface {
public void say(String username, org.apache.thrift.async.AsyncMethodCallback<String> resultHandler) throws org.apache.thrift.TException;
}

public static class Client extends org.apache.thrift.TServiceClient implements Iface {
public static class Factory implements org.apache.thrift.TServiceClientFactory<Client> {
public Factory() {
}

public Client getClient(org.apache.thrift.protocol.TProtocol prot) {
return new Client(prot);
}

public Client getClient(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {
return new Client(iprot, oprot);
}
}

public Client(org.apache.thrift.protocol.TProtocol prot) {
super(prot, prot);
}

public Client(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {
super(iprot, oprot);
}

public String say(String username) throws org.apache.thrift.TException {
send_say(username);
return recv_say();
}
// 省略.....
}

public static class AsyncClient extends org.apache.thrift.async.TAsyncClient implements AsyncIface {
public static class Factory implements org.apache.thrift.async.TAsyncClientFactory<AsyncClient> {
private org.apache.thrift.async.TAsyncClientManager clientManager;
private org.apache.thrift.protocol.TProtocolFactory protocolFactory;

public Factory(org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.protocol.TProtocolFactory protocolFactory) {
this.clientManager = clientManager;
this.protocolFactory = protocolFactory;
}

public AsyncClient getAsyncClient(org.apache.thrift.transport.TNonblockingTransport transport) {
return new AsyncClient(protocolFactory, clientManager, transport);
}
}

public AsyncClient(org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.transport.TNonblockingTransport transport) {
super(protocolFactory, clientManager, transport);
}

public void say(String username, org.apache.thrift.async.AsyncMethodCallback<String> resultHandler) throws org.apache.thrift.TException {
checkReady();
say_call method_call = new say_call(username, resultHandler, this, ___protocolFactory, ___transport);
this.___currentMethod = method_call;
___manager.call(method_call);
}
// 省略.....
}
// 省略.....
}

對於開發人員而言,使用原生的Thrift框架,僅需要關注以下四個核心內部接口/類:Iface, AsyncIface, Client和AsyncClient。

Iface:服務端通過實現HelloWorldService.Iface接口,向客戶端的提供具體的同步業務邏輯。
AsyncIface:服務端通過實現HelloWorldService.Iface接口,向客戶端的提供具體的異步業務邏輯。
Client:客戶端通過HelloWorldService.Client的實例對象,以同步的方式訪問服務端提供的服務方法。
AsyncClient:客戶端通過HelloWorldService.AsyncClient的實例對象,以異步的方式訪問服務端提供的服務方法。
(二) 新建Maven工程
a). 新建maven工程,引入thrift的依賴,這里使用的是版本0.10.0。

<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.10.0</version>
</dependency>

b). 將生成類的HelloWorldService.java源文件拷貝進項目源文件目錄中,並實現HelloWorldService.Iface的定義的say()方法。

HelloWorldServiceImpl.java

public class HelloWorldServiceImpl implements HelloWorldService.Iface {
@Override
public String say(String username) throws TException {
return "Hello " + username;
}
}

c). 服務器端程序編寫:

SimpleServer.java

public class SimpleServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(ServerConfig.SERVER_PORT);
TServerSocket serverTransport = new TServerSocket(serverSocket);
HelloWorldService.Processor processor =
new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl());

TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();
TSimpleServer.Args tArgs = new TSimpleServer.Args(serverTransport);
tArgs.processor(processor);
tArgs.protocolFactory(protocolFactory);

// 簡單的單線程服務模型 一般用於測試
TServer tServer = new TSimpleServer(tArgs);
System.out.println("Running Simple Server");
tServer.serve();
}
}

d). 客戶端程序編寫:

SimpleClient.java

public class SimpleClient {
public static void main(String[] args) {
TTransport transport = null;
try {
transport = new TSocket(ServerConfig.SERVER_IP, ServerConfig.SERVER_PORT, ServerConfig.TIMEOUT);
TProtocol protocol = new TBinaryProtocol(transport);
HelloWorldService.Client client = new HelloWorldService.Client(protocol);
transport.open();

String result = client.say("Leo");
System.out.println("Result =: " + result);
} catch (TException e) {
e.printStackTrace();
} finally {
if (null != transport) {
transport.close();
}
}
}
}

e). 運行服務端程序,服務端在指定端口監聽客戶端的連接請求,控制台輸出啟動日志:

 

f). 運行客戶端程序,客戶端通過網絡請求HelloWorldService的say()方法的具體實現,控制台輸出返回結果:

 

這里使用的一個基於單線程同步的簡單服務模型,一般僅用於入門學習和測試!

總結
本文對Thrift的概念做了相關介紹,體驗了一番thrift程序如何編寫!
————————————————

原文鏈接:https://blog.csdn.net/baidu_22254181/article/details/82814489


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM