《精通併發與Netty》學習筆記(06 - Apache Thrift使用簡介)

1、概述java

Apache Thrift 是 Facebook 實現的一種高效的、支持多種編程語言的遠程服務調用的框架。Thrift是由Facebook開發的,並在2008年捐給了Apache基金會,成爲了一個孵化器項目。python

Thrift 主要用於各個服務之間的RPC通訊,支持跨語言,經常使用的語言好比C++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#,Cocoa,JavaScript,Node.js,Smalltalk,and OCaml都支持。
Thrift是一個典型的CS(客戶端/服務端)結構,客戶端和服務端可使用不一樣的語言開發,既然客戶端和服務端能使用不一樣的語言開發,
那麼必定就要有一種中間語言來聯繫客戶端和服務端的語言,這種語言就是IDL(Interface Description Language)git

Thrift是一個軟件框架,用來進行可擴展且跨語言的服務的開發。它結合了功能強大的軟件堆棧和代碼生成引擎,Thrift是一個驅動層接口,它提供了用於客戶端使用多種語言實現的API。apache

Thrift是個代碼生成庫,支持的客戶端語言包括C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 。它的目標是爲了各類流行的語言提供便利的RPC調用機制,而不須要使用那些開銷巨大的方式,好比SOAP。編程

2、Thrift的特性
一、語言無關的類型
由於類型是使用定義文件按照語言中立的方式規定的,因此它們能夠被不一樣的語言分析。好比,C++的結構能夠和Python的字典類型相互交換數據。
二、通用傳輸接口
不論你使用的是磁盤文件、內存數據仍是socket流,均可以使用同一段應用代碼。
三、協議無關
Thrift會對數據類型進行編碼和解碼,能夠跨協議使用。
四、支持版本
數據類型能夠加入版本信息,來支持客戶端API的更新。數組

3、Thrift的數據類型

Thrift 腳本可定義的數據類型包括如下幾種類型:服務器

基本類型:
  bool: 布爾值
  byte: 8位有符號整數
  i16: 16位有符號整數
  i32: 32位有符號整數
  i64: 64位有符號整數
  double: 64位浮點數
  string: UTF-8編碼的字符串
  binary: 二進制串
結構體類型:
  struct是Thrift IDL中的基本組成塊,由域組成,每一個域有惟一整數標識符,類型,名字和可選的缺省參數組成。如定義一個相似於Twitter服務:多線程

struct Tweet {
    1: required i32 userId;                  // (1)
    2: required string userName;             // (2)
    3: required string text;
    4: optional Location loc;                // (3)
    16: optional string language = "english" // (4)
}

struct Location {                            // (5)
    1: required double latitude;
    2: required double longitude;
}

容器類型:框架

  list: 有序元素列表
  set: 無序無重複元素集合
  map: 有序的key/value集合
異常類型:
  exception: 異常類型
服務類型:
  service: 具體對應服務的類
Thrift的協議
Thrift可讓用戶選擇客戶端與服務端之間傳輸通訊協議的類別,在傳輸協議上整體劃分爲文本(text)和二進制(binary)傳輸協議。爲節約帶寬,提升傳輸效率,通常狀況下使用二進制類型的傳輸協議爲多數,有時還會使用基於文本類型的協議,這須要根據項目/產品中的實際需求。經常使用協議有如下幾種:異步

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

TSocket:使用阻塞式I/O進行傳輸,是最多見的模式
TNonblockingTransport:使用非阻塞方式,用於構建異步客戶端
TFramedTransport:使用非阻塞方式,按塊的大小進行傳輸,相似於Java中的NIO
Thrift的服務端類型
TSimpleServer:單線程服務器端,使用標準的阻塞式I/O
TThreadPoolServer:多線程服務器端,使用標準的阻塞式I/O
TNonblockingServer:單線程服務器端,使用非阻塞式I/O
THsHaServer:半同步半異步服務器端,基於非阻塞式IO讀寫和多線程工做任務處理
TThreadedSelectorServer:多線程選擇器服務器端,對THsHaServer在異步IO模型上進行加強

4、Thrift的工做原理

定義thrift的文件,由thrift文件(IDL)生成雙方語言的接口、model,在生成的model以及接口中會有解碼編碼的代碼

5、Thrift案例解析

第一步:打開idea編寫data.thrift文件 

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 dataException),
    void savePerson(1: required Person person) throws (1: DataException dataException)
}

第二步:打開ides設置File——Setting——Tools——Terminal——Shell path中爲cmd.exe路徑,通常爲C:\WINDOWS\system32目錄下

第三步:打開終端,輸入命令:thrift --gen java src/thrift/data.thrift 生成java文件

 第四步:將生成java文件拷貝到項目中,並修改build.gradle文件增長對thrift的引用

 第五步:編寫實現類PersonServiceImpl

package com.ssy.netty.thrift;

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

public class PersonServiceImpl implements PersonService.Iface {
    @Override
    public Person getPersonByUsername(String username) throws DataException, TException {
        System.out.println("Get client param:"+username);
        Person person = new Person();
        person.setUsername(username);
        person.setAge(20);
        person.setMarried(false);
        return person;
    }

    @Override
    public void savePerson(Person person) throws DataException, TException {
        System.out.println("Get client param:");
        System.out.println(person.getUsername());
        System.out.println(person.getAge());
        System.out.println(person.isMarried());
    }
}

第六步:編寫服務端程序ThriftServer,這裏咱們服務端和客戶端都採用java編寫

package com.ssy.netty.thrift;

import generated.PersonService;
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;

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));

        TServer server = new THsHaServer(arg);
        System.out.println("Thrift Server Started!");
        server.serve();
    }
}

第七步:編寫客戶端ThriftClient

package com.ssy.netty.thrift;

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

public class ThriftClient {
    public static void main(String[] args) {
        TTransport transport = new TFramedTransport(new TSocket("localhost",8899),600);
        TProtocol protocol = new TCompactProtocol(transport);
        PersonService.Client client = new PersonService.Client(protocol);

        try {
            transport.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 e){
            throw new RuntimeException(e.getMessage(),e);
        }finally {
            transport.close();
        }
    }
}

第八步:分別運行服務端和客戶端查看效果

   

 經過本節案例,咱們經過java編寫的服務端和客戶端實現了基於Thrift的通訊,下節咱們分別用java和python來實現一樣的功能。

相關文章
相關標籤/搜索