Thrift 簡明教程

現現在對於任何一個大型的服務,都不太多是一個單體的服務。而是由諸多的子服務構成,具體的業務邏輯經過子服務之間的相互調用來完成。這種相互的調用稱之爲遠程調用,也就是一般所說的 RPC。java

什麼是 RPC

RPC(Remote Procedure Call)是指遠程調用,這個和本地調用有很大的區別。下面在 main 方法中調用 helloWorld 就是本地調用。apache

public class Main {
    public static void final main(String[] args) {
        helloWorld(); // 本地調用
    }
    public static void helloWorld() {
        System.out.println("Hello World");
    }
}
複製代碼

如今假設 helloWorld 這個方法不在本地,無法直接調用,那麼就須要經過遠程調用來訪問這個方法,在進行遠程調用的過程當中,就產生了客戶端-服務端(Server-Client)模式,服務的提供方就是服務端,服務的調用方就是客戶端,客戶端和服務端並非絕對不變的,要看具體的服務的調用流程來認定服務端和客戶端。編程

遠程調用的方式有不少,好比 Java 中的 RMI(Remote Method Invocation) 就是典型的 RPC, 還有 Http 也能夠認爲是一種 RPC。服務器

Thrift 是 FaceBook 實現的一種支持多語言的 RPC 框架,對於主流的編程語言 Java ,C++, Python 等都有很好的支持。 Thrift 會經過自身的 IDL(Interface Description Language) 語言來對接口進行定義,而後 Thrift 經過定義好的 IDL 文件生成相應的接口腳手架,而後只須要分別實現接口腳手架的 Server 端和 Client 端。並將 Server 端進行部署,Client 就能夠訪問 Server端的服務。網絡

Thrift 特性簡介

下圖是 Thrift 官方給出的架構圖。Thrift 支持的語言多達 28 種,對於各種操做系統也都提供了支持,Thrift 將自身劃爲4層(不要和網絡的四層協議混淆),並且對於各個層已經作好乾淨的分層和實現。對於不一樣的業務場景就能夠選擇不一樣的協議進行組合。數據結構

Thrift 總體上能夠分紅如下四層:架構

  • 傳輸層提供了面向網絡 IO 的抽象,在傳輸層可使用多種傳輸協議,TCP、Http 提供了支持
  • 協議層中定義了內存中數據到傳輸格式的映射機制
  • 處理層封裝了從輸入流讀取數據和從輸出流寫數據的能力
  • 服務器層將上述的組件合併到一塊兒並按照下列的流程工做:
    • 建立一個 Transport 對象
    • 在 Transport 基礎上建立輸入\輸出 Protocol 對象
    • 在輸入\輸出 Protocol 基礎上建立 Processor 對象
    • 監聽到來的鏈接並移交給 Processor 處理
Thrift 的性能

Thrift 的性能在現有的 RPC 框架中屬於前列,這裏有詳細的 Benchmark,若是特別關注性能,那麼 Thrift 是一個很好的選擇。併發

Thrift 的網絡模型

並且 Thrift 提供了多種網絡模型,支持阻塞服務模型非阻塞服務模型,全部的網絡處理模型都繼承自 TServer框架

阻塞服務模型異步

  • TSimpleServer
    • 最簡單的阻塞 IO 模型
    • 一次只能接收和處理一個請求
    • 實際開發基本不會用到
  • TThreadPoolServer
    • 採用阻塞 Socket 的方式工做
    • 主線程負責監聽是否有新的 Socket 到達
    • 具體的業務處理交給線程池來處理

非阻塞服務模型

  • TNonblockingServer
    • 單線程模式,可是引入了 NIO 的機制,經過 Channel/Selector 機制來處理事件
  • THsHaServer
    • THsHaServer 繼承自 TNonblockingServer
    • 引入了線程池提升了任務的併發處理能力
  • TThreadedSelectorServer
    • 這個模型是對 THsHaServer 模型的一種補充

Thrift的序列化機制

數據在網絡的傳輸過程當中,序列化和反序列化是一個很重要的過程,對於不一樣的系統,對序列化有着不一樣的要求,Thrift 提供了多種序列化的方式來知足不一樣的要求。傳輸協議整體上能夠分紅文本二進制的兩種協議。

Thrift 有以下的傳輸協議:

  • TBinaryProtocol:二進制編碼格式進行數據傳輸
  • TCompactProtocol:高效率、密集的二進制編碼格式進行數據傳輸
  • TJSONProtocol:使用 JSON 文本的數據編碼協議進行數據傳輸
  • TSimpleJSONProtocol:只提供 JSON 只寫的協議,適用於經過腳本語言進行解析

Thrift demo 搭建

Thrift 的安裝在官方文檔已經介紹的很清楚了。

Thrift 使用 IDL 來定義(Client-Server)之間的接口。假如須要定義一個 hello 的接口,那麼就能夠定義一個 hello.thrift 文件,文件的內容以下:

service HelloWorldService {
    string hello(1: string name)
}
複製代碼

上面定義了一個名爲 HelloWorldService 的服務,在這個服務器中有一個叫 hello 的接口,這個接口接受一個 string 類型的參數。

servicestring 都是 IDL 的關鍵字,IDL 中的關鍵字包括:

  • 基本數據類型
  • 容器類型
  • 結構體
  • 枚舉

這些都是 IDL 的基本數據結構,也還有命名空間、異常、常量等關鍵字。完整的 IDL文檔看這裏

上面的 IDL 代碼定義好了一個 Thrift 接口的規則,Thrift 就能夠根據這個規則生成相應的接口文件。經過這些接口文件,就能夠方便的實現相應的業務邏輯,至關於 Thrift 經過 IDL 文件搭出了服務腳手架。

在生成的腳手架文件中,有兩個 interface 很是重要:

  • Iface: 這個 interface 由服務器來實現,向客戶端提供服務的邏輯在這個接口內實現
  • Client: 這個 interface 由客戶端來實現,訪問服務端的邏輯在這個接口內實現

AsyncIfaceAsyncClient 是這兩個 interface 的異步版本。

下面使用 SimpleServer 來實現這個服務:

Iface 實現:

public class HelloWorldServiceImpl implements HelloWorldService.Iface{
    @Override
    public String say(String username) throws TException {
        return "Hello " + username;
    }
}

/** *建立一個服務器,向外暴露服務 */
public class HelloWorldServer {
    public static void main(String[] args) throws TTransportException {
        TServerSocket serverSocket = new TServerSocket(9090);
        HelloWorldService.Processor processor = new HelloWorldService.Processor(new HelloWorldServiceImpl());
        TSimpleServer.Args tArgs = new TSimpleServer.Args(serverSocket);
        tArgs.processor(processor);
        TServer server = new TSimpleServer(tArgs);
        server.serve();
    }
}
複製代碼

Client實現

/** * 客戶端,鏈接服務器,而且發送消息 */
public class HelloWorldClient {
    public static void main(String[] args) throws TTransportException, TException {
        TTransport transport = new TSocket("127.0.0.1", 9090, 3000);
        TProtocol protocol = new TBinaryProtocol(transport);
        HelloWorldService.Client client = new HelloWorldService.Client(protocol);
        try {
            transport.open();
            String result = client.say("Ray");
            System.out.println(result);
        } finally {
            if (null != transport) {
                transport.close();
            }
        }
    }
}
複製代碼

一個簡單的 Thrift 的服務也就實現了。

(完)

相關文章
相關標籤/搜索