前言:
因為項目需要跨語言,c++客戶端,web服務端,遠程調用等需求,所以用到了RPC框架Thrift,剛開始有點虛,第一次接觸RPC框架,后來沒想到Thrift開發方便上手快,而且性能和穩定性也不錯,項目也順利完成。所以給各位小白們,“科普”一下如何使用Thrift完成自己的遠程調用。
1.什么是RPC:
平時開發的服務,大多都是本地調用,如果說需要依賴他人服務了,而且他人的服務在遠端,那怎么調用呢?
RPC能夠游刃有余的解決這樣的問題。首先來研究一下什么RPC。
RPC(remote produce call),遠程過程調用協議。它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。
看下面這張圖:

一次遠程的調用經歷了一下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的大門吧
2.What is 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級別的客戶端與服務端
3.How to use Thrift?
首先要做的就是下載並配置Thrift,附上鏈接:http://thrift.apache.org/download
現在版本都是0.10.0,
下載好之后將名字改成“thrift.exe”,
我的電腦操作系統是Windows,屬於在Windows情況下配置。
將thrift.exe放在Thrift文件夾下:
配置環境變量:

配置完成之后,打開dos窗口,輸入”thrifx -version”:

接下來執行三步走策略:
第一步:通過IDL定義數據結構和服務
從最簡單的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
