1. 程式人生 > 其它 >Apache Thrift 安裝及快速入門

Apache Thrift 安裝及快速入門

Apache Thrift是什麼?

The Apache Thrift software framework, for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages.

Apache Thrift軟體框架用於可擴充套件的跨語言服務開發,將軟體堆疊與程式碼生成引擎相結合,構建可在C ++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#之間高效無縫工作的服務, Cocoa,JavaScript,Node.js,Smalltalk,OCaml和Delphi等語言。

Thrift最初由facebook研發,主要用於各個服務之間的RPC通訊,支援跨語言,支援的語言有C++,Java,Python,PHP,Ruby,Erlang,PErl,Haskell,C#,Cocoa,JavaScript,Node.js,
Smalltalk,and OCaml都支援。

Thrift是一個典型的CS(客戶端/服務端)結構,客戶端和服務端可以使用不同的語言開發。既然客戶端和伺服器端能使用不同的語言開發,那麼一定就要有一種中間語言來關聯客戶端和伺服器端的語言。這種語言就是IDL(Interface Description Language)。

Thrift不支援無符號型別,因為很多程式語言不存在無符號型別,比如說java。一個RPC框架如果支援多種語言,那麼這個RPC框架所支援的資料型別一定是這個RPC框架多語言支援的資料型別的交集。

Apache Thrift的一些概念

Thrift支援的資料型別

bool: 布林型別(true或者false)
byte: 有符號位元組
i16: 16位有符號整數
i32: 32位有符號整數
i64: 64位有符號整數
double: 64位浮點數
string: 字串

集合中的元素可以是除了service之外的任何型別,包括exception。這邊的service和exception是Thrift支援的元件,Thrift支援三種元件,分別是Structs(結構體),Service(客戶端和服務端通訊的介面),exception(客戶端和服務端通訊介面丟擲的異常)

結構體(struct)
就像C語言一樣,Thrift支援struct型別,目的就是將一些資料聚合在一起,方便傳輸管理,struct的定義形式如下:

struct People{
    1:string name;
    2:i32 age;
    3:string gender;
 }

列舉(enum)
列舉的定義形式和Java的Enum定義類似

enum Gender{
    MALE,
    FEMALE
}

異常(exception)
Thrift支援自定義exception,規則與struct一樣

exception RequestException{
    1: i32 code;
    2: string reason;
 }

服務(service)
Thrift定義服務相當於Java中建立Interface一樣,建立的service經過程式碼生成命令之後就會生成客戶端和伺服器端的框架程式碼。定義形式如下:

service HelloWorldService{
    //service中定義的函式,相當於Java Interface中定義的方法
    string doAction(1:string name,2:i32 age);
}

型別定義
Thrift支援類似C++一樣的typedef定義,比如我們對i32不熟悉,我們就使用int類代替i32,比如我們對i64不熟悉,我們就使用long代替i64

typedef i32 int 
typedef i64 long

常量(const)
Thrift也支援常量定義,使用const關鍵字

const i32 MIN_GATE=30
const string MY_WEBSITE="http://facebook.com"

名稱空間
Thrift的名稱空間相當於java中的package的意思,主要目的是組織程式碼。Thift使用關鍵字namespave定義名稱空間:

namespace java com.test.thift.demo

格式是:namespace 語言名 路徑

檔案包含
Thrift也支援檔案包含,相當於C/C++中的include,java中的import。使用關鍵字include定義:

include "global.thift"

註釋
Thrift註釋方式支援shell風格的註釋,支援C/C++風格的註釋,即#和開頭的語句都當作註釋,/**/包裹的語句也是註釋。

可選與必選
Thrift提供兩個關鍵字required,optional,分別用於表示對應的欄位是必填的還是可選的

struct People{
    1:required string name;
    2:optional i32 age;
}

Thrift傳輸格式(協議)

  1. TBinaryProtocal-二進位制格式
  2. TCompactProtocol-壓縮格式
  3. TJSONProtocol-JSON格式
  4. TSimpleJSONProtocol-提供JSON只寫協議,生成的檔案很容易通過指令碼語言解析
  5. TDebugProtocol-使用易懂的可讀文字格式,以便於debug

Thrift資料傳輸方式

  1. TSocket-阻塞式socket
  2. TFramedTransport-以frame為單位進行傳輸,非阻塞式服務中使用
  3. TFileTransport-以檔案形式進行傳輸
  4. TMemoryInputTransport-將記憶體用語I/O,Java實現時內部實際使用了簡單的ByteArrayOutputStream。
  5. TZlibTransport-使用zlib進行壓縮,與其他傳輸方式聯合使用。當前無java實現。

Thrift支援的服務模型

  1. TThreadPoolServer - 簡單的單執行緒服務模型,常用於測試
  2. TSimpleServer - 多執行緒服務模型,使用標準的阻塞式IO
  3. TNonblockingServer - 多執行緒服務模型,使用非阻塞式IO(需要使用TFramedTransport資料傳輸方式)
  4. THsHaServer-THsHa引入了執行緒池去處理,其模型把讀寫任務放到執行緒池處理;Half-sync/Half-async的處理模式,Half-async是在處理IO事件上
    (accept/read/write io),Half-sync用於handler對rpc的同步處理。

注意
一般在工作中使用TCompactProtocol傳輸協議,使用TFramedTransport資料傳輸方式,使用THsHaServer服務模型。

Thrift支援的容器型別

list:一系列由T型別的資料組成的有序列表,元素可以重複。
set:一系列由T型別的資料組成的無序集合,元素不可重複。
map:一個字典結構,key為k型別,value為V型別,相當於java中的HashMap
以上集合容器都可以使用泛型的。

Thrift工作原理

如何實現多語言之間的通訊?
資料傳輸實現socket(多種語言均支援),資料再以特定的格式(String等)傳送,接收方語言進行解析。
Apache Thrift定義的thrift的檔案(IDL),由thrift檔案(IDL)生成雙方語言的介面,model,在生成的model以及介面中會有解碼編碼的程式碼。

Thrift 架構

  Thrift架構圖

Thrift的安裝

官方網站提供的下載安裝地址,根據不同的作業系統選擇自己的安裝方式

mac電腦推薦使用更簡單的安裝方式Homebrew工具,
Homebrew官方網址

先安裝Homebrew:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安裝成功之後,檢測Homebrew:

➜  ~ which brew
/usr/local/bin/brew

安裝Apache Thrift

➜  ~ brew install thrift

安裝完成之後:
檢視具體安裝資訊:

➜  ~ which thrift
/usr/local/bin/thrift
➜  ~ thrift --version
Thrift version 0.10.0
thrift --help

快速入門

定義idl檔案

先定義一個idl檔案(介面描述檔案),定義了結構體(struct),異常(exception)和服務(service)

namespace java thrift.generated

typedef i16 short
typedef i32 int
typedef i64 long
typedef bool boolean
typedef string String

struct Person{
    1: optional String username,
    2: optional int age,
    3: optional boolean married
}

exception DataException{
    1: optional String message,
    2: optional String callStack,
    3: optional String date
}

service PersonService{
    Person getPersonByUsername(1: required String username) throws (1: DataException dateException),

    void savePerson(1: required Person person) throws (1: DataException dataException)
}

使用thrift編譯器生成編譯檔案

thrift --gen java src/thrift/data.thrift
  java生成的程式碼

將生成的程式碼複製到src/main目錄下,發現報錯,加入java的依賴pom檔案:

<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.10.0</version>
</dependency>

使用java編寫客戶端與伺服器端的程式碼

編寫介面實現類,實際開發中放在服務端

import org.apache.thrift.TException;
import thrift.generated.DataException;
import thrift.generated.Person;
import thrift.generated.PersonService;

public class PersonServiceImpl implements PersonService.Iface{

    @Override
    public Person getPersonByUsername(String username) throws DataException, TException {
        System.out.println("Got client Param:" + username);

        Person person = new Person();
        person.setUsername(username);
        person.setAge(32);
        person.setMarried(true);

        return person;
    }

    @Override
    public void savePerson(Person person) throws DataException, TException {
        System.out.println("Got Client Param: ");

        System.out.println(person.getUsername());
        System.out.println(person.getAge());
        System.out.println(person.isMarried());
    }
}

伺服器端程式碼:

import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import thrift.generated.PersonService;

public class ThriftServer {
    public static void main(String[] args) throws Exception{

        TNonblockingServerSocket socket = new TNonblockingServerSocket(8899);
        THsHaServer.Args arg = new THsHaServer.Args(socket).minWorkerThreads(2).maxWorkerThreads(4);
        //範型就是實現的接收類
        PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl());

        //表示協議層次(壓縮協議)
        arg.protocolFactory(new TCompactProtocol.Factory());
        //表示傳輸層次
        arg.transportFactory(new TFramedTransport.Factory());
        arg.processorFactory(new TProcessorFactory(processor));

        //半同步半非同步的server
        TServer server = new THsHaServer(arg);

        System.out.println("Thrift Server started!");

        //死迴圈,永遠不會退出
        server.serve();
    }
}

客戶端程式碼:

import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFastFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import thrift.generated.Person;
import thrift.generated.PersonService;

//服務端的協議和客戶端的協議要一致
public class ThriftClient {
    public static void main(String[] args) {

        TTransport tTransport = new TFastFramedTransport(new TSocket("localhost",8899),600);
        TProtocol tProtocol = new TCompactProtocol(tTransport);
        PersonService.Client client = new PersonService.Client(tProtocol);

        try{
            tTransport.open();

            Person person = client.getPersonByUsername("張三");

            System.out.println(person.getUsername());
            System.out.println(person.getAge());
            System.out.println(person.isMarried());

            System.out.println("............");

            Person person2 = new Person();

            person2.setUsername("李四");
            person2.setAge(30);
            person2.setMarried(true);

            client.savePerson(person2);
        }catch (Exception ex){
            throw new  RuntimeException(ex.getMessage(),ex);
        }finally {
            tTransport.close();
        }
    }
}

啟動伺服器,再啟動客戶端,
客戶端列印:

Received 1
張三
32
true
............
Received 2

伺服器端列印:

Thrift Server started!
Got client Param:張三
Got Client Param: 
李四
30
true

跟我們之前的Google Protobuf相比,Google Protobuf只是進行編解碼(序列化與反序列)操作,使用netty作為網路載體,進行遠端方法呼叫。而Thrift不僅僅既可以進行編解碼工作,還提供傳輸物件功能,並且可以自己定義業務介面。



作者:二月_春風
連結:https://www.jianshu.com/p/bfcba5f0eaa1
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。