1. 概述
thrift是一個軟件框架,用來進行可擴展且跨語言的服務的開發。它結合了功能強大的軟件堆棧和代碼生成引擎,以構建在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 這些編程語言間無縫結合的、高效的服務。Thrift允許定義一個簡單的定義文件中的數據類型和服務接口,以作為輸入文件,編譯器生成代碼用來方便地生成RPC客戶端和服務器通信的無縫跨編程語言。thrift最初由facebook開發用做系統內各語言之間的RPC通信 。2007年由facebook貢獻到apache基金 ,08年5月進入apache孵化器 。
2. thrift 的跨語言特性
thrift通過一個中間語言IDL(接口定義語言)來定義RPC的數據類型和接口,這些內容寫在以.thrift結尾的文件中,然后通過特殊的編譯器來生成不同語言的代碼,以滿足不同需要的開發者,比如java開發者,就可以生成java代碼,c++開發者可以生成c++代碼,生成的代碼中不但包含目標語言的接口定義,方法,數據類型,還包含有RPC協議層和傳輸層的實現代碼。
3. thrift 的協議棧結構
thrift是一種c/s的架構體系.在最上層是用戶自行實現的業務邏輯代碼.第二層是由thrift編譯器自動生成的代碼,主要用於結構化數據的解析,發送和接收。TServer主要任務是高效的接受客戶端請求,並將請求轉發給Processor處理。Processor負責對客戶端的請求做出響應,包括RPC請求轉發,調用參數解析和用戶邏輯調用,返回值寫回等處理。從TProtocol以下部分是thirft的傳輸協議和底層I/O通信。TProtocol是用於數據類型解析的,將結構化數據轉化為字節流給TTransport進行傳輸。TTransport是與底層數據傳輸密切相關的傳輸層,負責以字節流方式接收和發送消息體,不關注是什么數據類型。底層IO負責實際的數據傳輸,包括socket、文件和壓縮數據流等。
4. thrift java 實例
4.1 創建一個服務HelloWorld
(1)創建文件Hello.thrift,代碼如下:
namespace java org.wavemelody.thrift.demo service HelloWorldService { string sayHello(1:string username) }
這里定義了一個HelloWorldService 服務,包含一個sayHello方法,入參和返回值都是一個string類型的參數。
(2)thrift生成代碼
thrift-0.11.0.exe -r -gen java HelloWorldService.thrift
thrift-0.11.0.exe是官網提供的windows下編譯工具,運用這個工具生成相關代碼,生成后的目錄結構如下:
thrift-0.11.0.exe
HelloWorldService.thrift
gen-java
|--org
|--wavemelody
|--thrift
|--demo
|--HelloWorldService.java
將生成的 HelloWorldService.java 拷貝到自己的工程中,注意包路徑。
(3)實現Iface
HelloWorldServiceImpl.java
package org.wavemelody.thrift.demo; import org.apache.thrift.TException; /** * Created by Andy on 2018/8/14. */ public class HelloWorldServiceImpl implements HelloWorldService.Iface{ @Override public String helloString(String para) throws TException { return "Hello " + para; } }
(4) 創建TSimpleServer服務端
創建服務器端實現代碼,將 HelloServiceImpl 作為具體的處理器傳遞給 Thrift 服務器,代碼如下:
HelloServiceServer.java
package org.wavemelody.thrift.demo; import org.apache.thrift.TProcessor; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TSimpleServer; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; /** * Created by Andy on 2018/8/14. */ public class HelloServiceServer { public void startServer(){ try {
System.out.println("HelloWorldServer start ... "); TServerSocket serverTransport = new TServerSocket(8888); TServer.Args tArgs = new TServer.Args(serverTransport); TProcessor tProcessor = new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl()); tArgs.processor(tProcessor); tArgs.protocolFactory(new TBinaryProtocol.Factory()); // tArgs.protocolFactory(new TCompactProtocol.Factory()); // tArgs.protocolFactory(new TJSONProtocol.Factory()); TServer server = new TSimpleServer(tArgs); server.serve(); } catch (TTransportException e) { e.printStackTrace(); } } public static void main(String[] args){ HelloServiceServer serviceServer = new HelloServiceServer(); serviceServer.startServer(); } }
(5)編寫客戶端Client代碼
HelloServiceClient.java
package org.wavemelody.thrift.demo; 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; /** * Created by Andy on 2018/8/14. */ public class HelloServiceClient { public void startClient(String username){ TTransport tTransport = null; try { tTransport = new TSocket("localhost",8888,30000); // 協議要和服務端一致 TProtocol protocol = new TBinaryProtocol(tTransport); // TProtocol protocol = new TCompactProtocol(tTransport); // TProtocol protocol = new TJSONProtocol(tTransport); HelloWorldService.Client client = new HelloWorldService.Client(protocol); tTransport.open(); String result = client.helloString(username); System.out.println("Thrify client result = " + result); } catch (TTransportException ex) { ex.printStackTrace(); } catch (TException ex) { ex.printStackTrace(); }finally { if(tTransport != null){ tTransport.close(); } } } public static void main(String[] args){ HelloServiceClient client = new HelloServiceClient(); client.startClient(" world"); } }
(6)測試遠程調用
啟動服務端,日志如下:
啟動客戶端,日志如下:
調用成功,符合預期結果。
4.2 TThreadPoolServer 服務模型
線程池服務模型,使用標准的阻塞式IO,預先創建一組線程處理請求。
ThreadPoolHelloServiceServer.java
package org.wavemelody.thrift.demo; import org.apache.thrift.TProcessor; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; /** * Created by Andy on 2018/8/14. */ public class ThreadPoolHelloServiceServer { public void startServer(){ try { System.out.println("HelloWorldServer start ... "); TServerSocket serverTransport = new TServerSocket(8888); TThreadPoolServer.Args tArgs = new TThreadPoolServer.Args(serverTransport); TProcessor tProcessor = new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl()); tArgs.processor(tProcessor); tArgs.protocolFactory(new TBinaryProtocol.Factory()); // 線程池服務模型,使用標准的阻塞式IO,預先創建一組線程處理請求。 TThreadPoolServer server = new TThreadPoolServer(tArgs); server.serve(); } catch (TTransportException e) { e.printStackTrace(); } } public static void main(String[] args){ ThreadPoolHelloServiceServer serviceServer = new ThreadPoolHelloServiceServer(); serviceServer.startServer(); } }
客戶端和之前的代碼一樣,測試結果如下:
4.3 TNonblockingServer 服務模型
使用非阻塞式IO,服務端和客戶端需要指定 TFramedTransport 數據傳輸的方式。
服務端代碼 TNonblockingHelloServiceServer.java
package org.wavemelody.thrift.demo; import org.apache.thrift.TMultiplexedProcessor; import org.apache.thrift.TProcessor; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.server.TNonblockingServer; import org.apache.thrift.transport.TFramedTransport; import org.apache.thrift.transport.TNonblockingServerSocket; import org.apache.thrift.transport.TTransportException; /** * Created by Andy on 2018/8/14. */ public class TNonblockingHelloServiceServer { public void startServer(){ try { System.out.println("HelloWorldServer start ... "); TNonblockingServerSocket tnbSocket = new TNonblockingServerSocket(8888); TNonblockingServer.Args tArgs = new TNonblockingServer.Args(tnbSocket); TProcessor tProcessor = new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl()); tArgs.processor(tProcessor); tArgs.protocolFactory(new TBinaryProtocol.Factory()); tArgs.transportFactory(new TFramedTransport.Factory()); // 線程池服務模型,使用標准的阻塞式IO,預先創建一組線程處理請求。 TNonblockingServer server = new TNonblockingServer(tArgs); server.serve(); } catch (TTransportException e) { e.printStackTrace(); } } public static void main(String[] args){ TNonblockingHelloServiceServer serviceServer = new TNonblockingHelloServiceServer(); serviceServer.startServer(); } }
客戶端代碼:TNonblockingHelloServiceClient.java
package org.wavemelody.thrift.demo; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TFramedTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; /** * Created by Andy on 2018/8/14. */ public class TNonblockingHelloServiceClient { public void startClient(String username){ TTransport tTransport = null; try { tTransport = new TFramedTransport(new TSocket("localhost",8888,30000)); // 協議要和服務端一致 TProtocol protocol = new TBinaryProtocol(tTransport); // TProtocol protocol = new TCompactProtocol(tTransport); // TProtocol protocol = new TJSONProtocol(tTransport); HelloWorldService.Client client = new HelloWorldService.Client(protocol); tTransport.open(); String result = client.helloString(username); System.out.println("Thrify client result = " + result); } catch (TTransportException ex) { ex.printStackTrace(); } catch (TException ex) { ex.printStackTrace(); }finally { if(tTransport != null){ tTransport.close(); } } } public static void main(String[] args){ TNonblockingHelloServiceClient client = new TNonblockingHelloServiceClient(); client.startClient(" world"); } }
測試結果: