Thrift
是一個輕量級、跨語言的遠程服務調用框架,最初由Facebook
開發,後面進入Apache
開源項目。它經過自身的IDL
中間語言, 並藉助代碼生成引擎生成各類主流語言的RPC
服務端/客戶端模板代碼。java
Thrift
支持多種不一樣的編程語言,包括C++
、Java
、Python
、PHP
、Ruby
等,本系列主要講述基於Java
語言的Thrift
的配置方式和具體使用。apache
Thrift
對軟件棧的定義很是的清晰, 使得各個組件可以鬆散的耦合, 針對不一樣的應用場景, 選擇不一樣是方式去搭建服務。編程
Thrift
軟件棧分層從下向上分別爲:傳輸層(Transport Layer
)、協議層(Protocol Layer
)、處理層(Processor Layer
)和服務層(Server Layer
)。後端
傳輸層(Transport Layer
):傳輸層負責直接從網絡中讀取和寫入數據,它定義了具體的網絡傳輸協議;好比說TCP/IP
傳輸等。緩存
協議層(Protocol Layer
):協議層定義了數據傳輸格式,負責網絡傳輸數據的序列化和反序列化;好比說JSON
、XML
、二進制數據等。bash
處理層(Processor Layer
):處理層是由具體的IDL
(接口描述語言)生成的,封裝了具體的底層網絡傳輸和序列化方式,並委託給用戶實現的Handler
進行處理。服務器
服務層(Server Layer
):整合上述組件,提供具體的網絡線程/IO服務模型,造成最終的服務。網絡
經過編寫RPC
接口Thrift IDL
文件,利用編譯生成器自動生成服務端骨架(Skeletons
)和客戶端樁(Stubs
)。從而省去開發者自定義和維護接口編解碼、消息傳輸、服務器多線程模型等基礎工做。多線程
Handler
)即實現類便可。IDL
定義好的客戶端樁和服務對象,而後就像調用本地對象的方法同樣調用遠端服務。經過維護Thrift
格式的IDL(接口描述語言)文件(注意寫好註釋),便可做爲給Client
使用的接口文檔使用,也自動生成接口代碼,始終保持代碼和文檔的一致性。且Thrift
協議可靈活支持接口的可擴展性。架構
由於其來自Google Protobuf
開發團隊,因此其IDL
文件風格相似Google Protobuf
,且更加易讀易懂;特別是RPC
服務接口的風格就像寫一個面向對象的Class
同樣簡單。
初學者只需參照: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
可讓用戶選擇客戶端與服務端之間傳輸通訊協議的類別,在傳輸協議上整體劃分爲文本(text
)和二進制(binary
)傳輸協議。爲節約帶寬,提升傳輸效率,通常狀況下使用二進制類型的傳輸協議爲多數,有時還會使用基於文本類型的協議,這須要根據項目/產品中的實際需求。經常使用協議有如下幾種:
JSON
文本的數據編碼協議進行數據傳輸JSON
只寫的協議,適用於經過腳本語言解析經常使用的傳輸層有如下幾種:
I/O
進行傳輸,是最多見的模式Java
中的NIO
I/O
I/O
I/O
IO
讀寫和多線程工做任務處理THsHaServer
在異步IO
模型上進行加強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
複製代碼
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
。
HelloWorldService.Iface
接口,向客戶端的提供具體的同步業務邏輯。HelloWorldService.Iface
接口,向客戶端的提供具體的異步業務邏輯。HelloWorldService.Client
的實例對象,以同步的方式訪問服務端提供的服務方法。HelloWorldService.AsyncClient
的實例對象,以異步的方式訪問服務端提供的服務方法。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
程序如何編寫!
歡迎關注技術公衆號: 零壹技術棧
本賬號將持續分享後端技術乾貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分佈式和微服務,架構學習和進階等學習資料和文章。