RPC遠端協議之Thrift入門
阿新 • • 發佈:2019-01-11
在上一篇文章《RPC遠端協議之原理分析》中,我介紹了RPC的工作原理及欲實現RPC框架功能應該做哪些事情,因為要做的事情太多,完全由開發人員研發實現,不是很現實,所以市面上出現了諸多RPC快捷框架,目前主流的有Facebook的Thrift、谷歌的gRPC,以及Dubbo,但就效能角度考慮,Thrift相對好些,並且是跨語言的,所以這裡先以Thrift的介紹開始。對於Facebook,我們現階段只需要知道它是一個高效能的、支援跨語言平臺的遠端服務呼叫框架,並且作為很多企業實現分散式系統架構的服務呼叫實現的基礎,以及該怎樣去使用它來快速搭建服務呼叫功能。
l 資料型別
l 準備條件
l 例子驗證
一、資料型別
我們知道,Thrift是跨語言的RPC開源框架,那麼它應該有自己的訊息資料型別,而不是其它任何一門語言的資料型別,否則就不能支援其它語言,那麼就看下它支援的幾種定製型別,具體如下:
1、基礎型別
為什麼Thrift的資料型別都是有符號?因為很多語言都不支援無符號的資料型別,所以Thrift為了滿足大部分語言特徵,所以沒必要加入無符號的資料型別。
2、特殊型別
binary,未經編碼的位元組流型別,主要針對字串型別的位元組流化,提供與java語言更好的互操作性。
3、結構型別
struct,定義普通的OOP型別,但不支援繼承特性。
4、容器型別
list,一種有序的列表集合型別,如:對應java的List
set,一種無序的唯一值集合型別,如:對應java的Set
map,一種離散的鍵值對集合型別,如:對應java的Map
5、服務型別
service,定義對外提供的服務,如:供客戶端使用的服務介面。
6、異常型別
exception,一種Thrift本身定製異常,與其它語言無縫結合的異常型別。
在下面的例子會演示基礎型別、容器型別、結構型別,以及服務型別的使用,供讀者參考。
二、準備條件
1、C/S雙端
這裡的C/S雙端指的是客戶端和服務端,實際使用時,客戶端與服務端程序往往不在同一個節點中,比如:分散式環境,所以客戶端和服務端一般是分離的,客戶端需要引用由服務端所生成的服務程式碼,來完成遠端呼叫。但在這裡,我們將客戶端與服務端程式碼均放在同一個專案中,所以客戶端不需要額外引入即可使用呼叫,當然兩端也分別在不同的程序中執行通訊。
2、編譯環境
下載地址:
http://thrift.apache.org/download
如果是mac osx則可以使用brew install thrift自動安裝;
如果是linux系統,則可使用apt-get install thrift-compiler安裝均可。
安裝後,可以使用thrift –version檢視版本,如果正常顯示,則安裝完成。
3、依賴新增
我這裡採用maven來載入和管理thrift依賴包,並且使用最新版本0.11.0,具體配置如下:
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.11.0</version>
</dependency>
三、例子驗證
在這裡,我就不以典型的helloworld為例,而是以根據使用者ID獲取使用者基本資訊和該使用者的訂單為例,詳細介紹下Thrift的幾種資料類的使用方法。
1、編寫IDL檔案(user.thrift)
# defaine the namespace
namespace java com.cwteam
# define the struct
struct User {
1:i32 uid,
2:string name,
3:i16 sex,
4:list<Order> orderList
}
struct Order {
1:string oid,
2:string oname,
3:double price,
4:i32 number,
5:string createAt
}
# define the service
service UserService {
# Get the user's order list by uid
User getUserOrders(1:i32 uid)
# Other operation follow here
# ...
}
2、生成語言檔案(UserService.java)
使用Thrift提供的編輯工具生成,切換到user.thrift檔案所在目錄,我的結構如下:
也就是user.thrift存放在main下,切換到main下,使用thrift命令編譯生成語言檔案,如下所示:
#thrift--gen java user.thrift
生成後的stub檔案如下樣子:
3、業務介面實現(UserHandler.java)
/**
* 使用者服務介面,由服務端負責實現
*/
public class UserHandler implements UserService.Iface {
public User getUserOrders(int uid) throws TException {
User user = new User(); // 模擬實現使用者及訂單查詢
user.setUid(uid);
user.setName("David Lang");
user.setSex((short)1);
List<Order> orders = new ArrayList<Order>();
Order order = new Order();
order.setOid("NO1112321");
order.setOname("《Thrift進階與提高》");
order.setPrice(99.99);
order.setNumber(1);
order.setCreateAt("2017-04-04");
orders.add(order);
Order order2 = new Order();
order2.setOid("NO1112322");
order2.setOname("《RPC進階與提高》");
order2.setPrice(88.88);
order2.setNumber(2);
order2.setCreateAt("2017-04-05");
orders.add(order2);
user.setOrderList(orders);
return user;
}
}
4、服務端例項(UserServer.java)
/**
* 將業務處理邏輯UserHandler作為具體的業務
* 處理器,傳遞給Thrift伺服器,執行處理邏輯.
*/
public class UserServer {
private static final int port = 9081;
private static UserHandler handler;
private static UserService.Processor processor;
/**
* 啟動服務端
* processor為控制呼叫邏輯
*/
public static void start(UserService.Processor processor) {
try {
// 阻塞方式,基於ServerSocket
TServerTransport serverTransport = new TServerSocket(port);
TServer server = new TSimpleServer(new TServer.Args(serverTransport).processor(processor));
System.out.println("Starting simple server ...");
// 啟動服務
server.serve();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
handler = new UserHandler();
processor = new UserService.Processor(handler);
start(processor);
}
}
5、客戶端例項(UserClient.java)
/**
* 遠端呼叫服務介面,獲取使用者資訊及訂單列表
*/
public class UserClient {
private static final int port = 9081;
private static final String addr = "localhost";
private static UserService.Client client;
private static TTransport transport;
/**
* 建立TTransport
*/
private static TTransport createTTransport() {
TTransport transport = new TSocket(addr,port);
return transport;
}
/**
* 開啟TTransport
*/
private static void openTTransport(TTransport transport) throws TTransportException {
if(null == transport) {
return;
}
transport.open();
}
/**
* 關閉TTransport
*/
private static void closeTTransport(TTransport transport) {
if(null == transport) {
return;
}
transport.close();
}
/**
* 建立客戶端實體
*/
private static UserService.Client createClient(TTransport transport) {
if(null == transport) {
return null;
}
// 編碼協議指定(這裡是二進位制方式傳遞)
TProtocol protocol = new TBinaryProtocol(transport);
if(null == protocol) {
return null;
}
// 設定編碼協議
UserService.Client client = new UserService.Client(protocol);
return client;
}
public static void main(String[] args) {
try {
transport = createTTransport();
openTTransport(transport);
client = createClient(transport);
// 呼叫遠端服務
if(null == client) {
System.out.println("客戶端生成失敗,不能呼叫服務 ...");
return;
}
User user = client.getUserOrders(10000021);
System.out.println(user);
} catch(TException e1) {
e1.printStackTrace();
}
}
}
6、例子執行結果
啟動服務端:
啟動客戶端:
好了,Thrift入門就介紹到這裡,讀者也會發現這裡所實現的服務呼叫方式為阻塞的,並且是非多執行緒的。
原文:https://blog.csdn.net/why_2012_gogo/article/details/79432630