1. 程式人生 > >RPC遠端協議之Thrift入門

RPC遠端協議之Thrift入門

在上一篇文章《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