由於項目須要跨語言,c++客戶端,web服務端,遠程調用等需求,因此用到了RPC框架Thrift,剛開始有點虛,第一次接觸RPC框架,後來沒想到Thrift開發方便上手快,並且性能和穩定性也不錯,項目也順利完成。因此給各位小白們,「科普」一下如何使用Thrift完成本身的遠程調用。html
平時開發的服務,大多都是本地調用,若是說須要依賴他人服務了,並且他人的服務在遠端,那怎麼調用呢?java
RPC可以遊刃有餘的解決這樣的問題。首先來研究一下什麼RPC。c++
RPC(remote produce call),遠程過程調用協議。它是一種經過網絡從遠程計算機程序上請求服務,而不須要了解底層網絡技術的協議。web
看下面這張圖:apache
一次遠程的調用經歷了一下10個步驟:服務器
1.調用客戶端以本地方式調用遠程服務網絡
2.client stub將請求(方法和參數)組裝成網絡消息數據結構
3.client stub找獲得服務器地址,將消息傳送到遠程主機併發
4.server stub獲得傳送過來的請求,進行解碼框架
5.server stub 調用本地服務,處理請求
6.本地服務處理請求,並將處理結果返回給server stub
7.server stub將請求處理結果組裝成網絡消息
8.server stub找到客戶端地址,將請求處理結果傳送給客戶端
9.client stub 接收到請求處理結果,進行解碼
10.客戶端最終接收到請求處理結果
RPC框架的目的就是將2-9步驟封裝起來,對使用者透明,客戶端只須要執行第一步調用接口,而後就可以獲得結果。這樣是否是很方便,並且省去了不少麻煩。
經過對RPC的初步瞭解,那接下來就開始不如Thrift的大門吧
Thrift是Facebook公司開發的一款開源的RPC框架,對於通常的RPC框架來講,經過IDL語言定義接口(Interface description language),Thrift也採用了這樣的作法,並經過一個編譯器生成不一樣語言的代碼(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),並由生成的代碼負責RPC協議層和傳輸層的實現。
Thrift協議棧:
第一部分(Your Code):
簡單總結:開發者的業務邏輯代碼
第二部分(ServiceClient)
Thrift自動生成的代碼,包含Processor、Tserver、ServiceClient
TServer負責接收Client的請求,並將請求轉發到Processor進行處理。TServer主要任務就是高效的接受Client的請求,特別是在高併發請求的狀況下快速完成請求。
Processor(或者TProcessor)負責對Client的請求作出相應,包括RPC請求轉發,調用參數解析和用戶邏輯調用,返回值寫回等處理步驟。Processor是服務器端從Thrift框架轉入用戶邏輯的關鍵流程。Processor同時也負責向Message結構中寫入數據或者讀出數據。
ServiceClient就是客戶端,包含能夠調用的請求方法和發送客戶端請求
第三部分:
TProtocol主要負責結構化數據組裝成Message,或者從Message結構中讀出結構化數據。TProtocol將一個有類型的數據轉化爲字節流以交給TTransport進行傳輸,或者從TTransport中讀取必定長度的字節數據轉化爲特定類型的數據。如int32會被TBinaryProtocol Encode爲一個四字節的字節數據,或者TBinaryProtocol從TTransport中取出四個字節的數據Decode爲int32。
第四部門:
TTransport負責以字節流方式發送和接收Message,是底層IO模塊在Thrift框架中的實現,每個底層IO模塊都會有一個對應TTransport來負責Thrift的字節流(Byte Stream)數據在該IO模塊上的傳輸。例如TSocket對應Socket傳輸,TFileTransport對應文件傳輸。
第五部分:
底層IO模塊,負責實際的數據傳輸,包括Socket,文件,或者壓縮數據流等。
經過這個協議棧,能夠得出結論,使用Thrift只須要作三件事:
1.經過IDL定義數據結構和服務
2.利用代碼生成工具生成代碼
3.編寫你的業務邏輯
接下來咱們就按照這個三步走開發一個簡單的HelloWorld級別的客戶端與服務端
首先要作的就是下載並配置Thrift,附上連接:http://thrift.apache.org/download
如今版本都是0.10.0,
下載好以後將名字改爲「thrift.exe」,
個人電腦操做系統是Windows,屬於在Windows狀況下配置。
將thrift.exe放在Thrift文件夾下:
配置環境變量:
配置完成以後,打開dos窗口,輸入」thrifx -version」:
接下來執行三步走策略:
從最簡單的HelloWorld開始,編寫HelloWorld.thrift,內容以下:
namespace java service.server service HelloWorld{ string sendString(1:string para) }
隨便放在一個文件夾下,我這裏放在E:\software\Thrift,
進入HelloWorld.thrift所在目錄,執行
執行完成,你會發現沒有任何提示,記得有位大神說過,沒有任何提示就是好事。
這時候發如今當前目錄下多了一個gen-java的目錄,裏面有Thrift生成的HelloWorld.java
OK,前兩步已經完成,仍是很簡單的吧。
建立一個Gradle管理的Java項目,bulid.gradle中添加相關的依賴,將gen-java中的HelloWorld.java拷貝到IDE的java項目中(注意包名,地址對應)。
(喜歡用Maven的朋友能夠用Maven構建)
dependencies { compile "org.apache.thrift:libthrift:0.9.2" compile "org.slf4j:slf4j-log4j12:1.7.5" }
建立HelloWorldServiceImpl實現HelloWorld.Iface接口,這個就是主要的業務邏輯。
package service.impl; import org.apache.thrift.TException; import service.server.HelloWorld; /** * 服務端實現 * * @author tang */ public class HelloWorldServiceImpl implements HelloWorld.Iface { @Override public String sendString(String para) throws TException { System.out.println("接收到服務端傳來的參數: " + para); String result = "服務端成功收到消息"; return result; } }
接着,建立服務端實現代碼,命名爲HelloWorldServiceServer,把HelloWoeldServiceImpl做爲一個具體的處理器傳遞給Thrift服務器:
package service.server; 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; /** * 服務端 * * @author tang */ import service.impl.HelloWorldServiceImpl; public class HelloWorldServiceServer { public static void main(String[] args) throws TTransportException { System.out.println("服務端開啓"); // 關聯處理器 TProcessor tProcessor = new HelloWorld.Processor<HelloWorld.Iface>(new HelloWorldServiceImpl()); // 設置服務端口爲 8080 TServerSocket serverSocket = new TServerSocket(8080); // 簡單的單線程服務模型 TServer.Args tArgs = new TServer.Args(serverSocket); tArgs.processor(tProcessor); // 設置協議工廠爲 TBinaryProtocol.Factory tArgs.protocolFactory(new TBinaryProtocol.Factory()); TServer server = new TSimpleServer(tArgs); // 啓動服務 server.serve(); } }
最後,再寫一個客戶端HelloWorldClient.java:
package client; 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 service.server.HelloWorld; /** * 客戶端 * * @author tang */ public class HelloWorldClient { public static void main(String[] args) { System.out.println("客戶端啓動...."); TTransport transport = null; try { // 設置調用的服務地址爲本地,端口爲8080,超時設置爲30秒 transport = new TSocket("localhost", 8080, 30000); // 協議要和服務端一致 TProtocol protocol = new TBinaryProtocol(transport); HelloWorld.Client client = new HelloWorld.Client(protocol); transport.open(); // 調用接口方法 String result = client.sendString("Hello World!"); System.out.println(result); } catch (Exception e) { e.printStackTrace(); } finally { if (null != transport) { transport.close(); } } } }
這個是project結構圖:
這裏有一些Thrift自帶的警告,不用去管他(有點違背Effective java中的原則)。。。。。
啓動服務端,而後再啓動客戶端,這是服務端會收到來自客戶端的消息:「HelloWorld」
客戶端收到服務端的反饋:
好了,到這裏,Thrift的第一個實例就結束了,總的來講Thrift仍是很好用的,有些的很差的地方歡迎批評斧正!
參考文章:
http://www.blogjava.net/ldwblog/archive/2014/12/03/421011.html