RPC框架 之 Apach thrift

Thrift

  • 1,Apache Thrift 主要用于各個(gè)服務(wù)之間的RPC通信,支持跨語言,常用語言:C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi都支持。

  • 2,Thrift 是一個(gè)典型的CS(客戶端/服務(wù)器)結(jié)構(gòu),客戶端和服務(wù)端可以使用不同的語言開發(fā),既然客戶端和服務(wù)端都能使用不同的語言開發(fā),那么一定就要有一種中間語言來關(guān)聯(lián)客戶端和服務(wù)端的語言,這種語言就是IDL(Interface Description Language), .thrift 就是一種IDL語言

Trift 數(shù)據(jù)類型(Base Types)

Trift 不支持無符號(hào)類型,因?yàn)楹芏嗑幊陶Z言不存在無符號(hào)類型,比如java,php
基礎(chǔ)數(shù)據(jù)類型
  • byte: 有符號(hào)字節(jié)。

  • i16: 16位有符號(hào)整數(shù)。

  • i32:32位有符號(hào)整數(shù)。

  • i64:64位有符號(hào)整數(shù)。

  • double: 64位 浮點(diǎn)數(shù)。

  • string: 字符串。

    1:optional string username,
    2:optional i32 age,

特殊數(shù)據(jù)類型
  • binary:一系列未編碼的字節(jié)

  • N.B.。

Structs結(jié)構(gòu)體
Thrift structs 定義了公共對象,也就是oop語言中的常說的類,但是他不具有繼承性.結(jié)構(gòu)體可以包含很多字段,字段包含的內(nèi)容: numeric field IDs, optional default values, 結(jié)構(gòu)體的目的就是將一些數(shù)據(jù)聚合在一起,方便傳輸管理
struct Person {
    1:optional string username,
    2:optional i32 age,
    3:optional bool married
}

容器類型(Containers)
  • list: 一系列由T類型的數(shù)據(jù)組成的有序列表,元素可以重復(fù)。

  • set: 一系列由T類型的數(shù)據(jù)組成的無序集合,元素不可重復(fù)。

  • map: 一個(gè)字典結(jié)構(gòu),key為K 類型,value為V 類型,相當(dāng)于java中的HashMap.

1: list<string> strings,
2: list<i32> newlist,
3: set<i32> newset,
4: set<string> a_set2500,
5: map<i32, i32> newmap,
6: map<string,string> map_field,

異常(Exceptions)
thift支持自定義exception,規(guī)則與struct一樣,
exception NotFoundException {
}

exception InvalidRequestException {
    1: required string why
}

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

服務(wù)(Services)
Thrift 定義服務(wù)相當(dāng)于java中創(chuàng)建Interface一樣,創(chuàng)建的service經(jīng)過代碼生成命令之后就會(huì)生成客戶端和服務(wù)器端的框架代碼。
  • 一個(gè)服務(wù)(Services)可以定義多個(gè)函數(shù)。
    service FacebookService {
        string getName(),
        map<string, i64> getCounters(),
        i64 getCounter(1: string key),
    }

    service PersonService{
        Person getPersonByUsername(1:required string username) throws(1:DataException dataException),

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

類型定義(typedef)
thirift 支持類似c++ 一樣的typedef定義,相當(dāng)于改了別名,語法:
typedef DefinitionType Identifier
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
}
常量(const)
thrift 也支持常量定義,使用const 關(guān)鍵字,語法:
const FieldType Identifier = ConstValue

const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}

命名空間
Thirft的命名空間相當(dāng)于java中package的意思,主要目的是組織代碼。thrift使用namespace定義命名空間:
namespace 語言名 路徑
    namespace java thrift.generated
    namespace php tutorial
文件包含
Trift也支持文件包含,相當(dāng)于php/c/c++中的include,java中的import.使用關(guān)鍵字include定義:
include “文件名”

include "paratent.thrift"

注釋
Trifit注釋方式支持shell風(fēng)格的注釋,支持c/c++ 風(fēng)格的注釋,即#和//開頭的語句都當(dāng)做注釋,/**/包含的語句就是注釋。
可選與必選
thrift 提供兩個(gè)關(guān)鍵字required,option,分別用于表示對應(yīng)的字段是必填的還是可選的。默認(rèn)是可選
struct People{
    1: required string name;
    2: optional i32 age;
}

一個(gè)完整的thrift 文件

namespace java thrift.generated
namespace php tutorial

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 dataEception),
    String testSave(1:required String name)
}


Thrift 工作原理

  • 如何實(shí)現(xiàn)多語言之間的通信?

  • 1,數(shù)據(jù)傳輸使用socket(多種語言均支持),數(shù)據(jù)再以特定的格式發(fā)送,接收語言進(jìn)行解析。

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

安裝thrift

brew install thrift

生成代碼

thrift -r --gen java src/thrift/person.thrift

thrift -r --java php src/thrift/person.thrift
或者
thrift -r --java php:server src/thrift/person.thrift

java thrift示例

idl users.thrift

namespace java com.lihao.netty.thrift2
namespace php thrift

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


struct User{
    1: String username,
    2: int age,
    3: int id,

}

struct UserRequest{
    1:int id;
    2:String token;
    3:String username;
    4:String passworld;
}


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

service UserService{
    User login(1:required UserRequest userRequst) throws(1: DataException dataException);
    void logOut(1:required UserRequest userRequst) throws(1: DataException dataException);
    list<User> userList()  throws(1: DataException dataException);
}


生成代碼

thrift -r --gen java src/thrift/users.thrift

實(shí)現(xiàn)service 接口方法 UserServiceIml.java

public class UserServiceIml implements UserService.Iface {
    @Override
    public User login(UserRequest userRequst) throws DataException, TException {
        System.out.println("--------服務(wù)器:login--------------");
        System.out.println(userRequst);
        User user =  new User();
        user.setId(2);
        user.setUsername("張三");
        user.setAge(20);
        return user;
    }

    @Override
    public void logOut(UserRequest userRequst) throws DataException, TException {
        System.out.println("--------服務(wù)器:logOut--------------");
        System.out.println(userRequst);


    }

    @Override
    public List<User> userList() throws DataException, TException {
        System.out.println("--------服務(wù)器:userList--------------");

      List userList =   new ArrayList<>();

        User user =  new User();
        user.setId(2);
       user.setUsername("我會(huì)回來的");
        user.setAge(20);

        User user1 =  new User();
        user1.setId(2);
        user1.setUsername("張三-2---");
        user1.setAge(20);

        User user2 =  new User();
        user2.setId(2);
        user2.setUsername("張三---123");
        user2.setAge(20);


        userList.add(user);
        userList.add(user1);
        userList.add(user2);
        return userList;
    }
}


服務(wù)端 Server.java


public class Server {
    public static void main(String... args) throws Exception {
        /**
         *  +-------------------------------------------+
         | Server                                    |
         | (single-threaded, event-driven etc)       |
         +-------------------------------------------+
         | Processor                                 |
         | (compiler generated)                      |
         +-------------------------------------------+
         | Protocol                                  |
         | (JSON, compact etc)                       |
         +-------------------------------------------+
         | Transport                                 |
         | (raw TCP, HTTP etc)                       |
         +-------------------------------------------+
         */
        TNonblockingServerSocket socket = new TNonblockingServerSocket(7788);

        THsHaServer.Args arg = new THsHaServer.Args(socket).minWorkerThreads(2).maxWorkerThreads(4);
        UserService.Processor<UserServiceIml> processor = new UserService.Processor<>(new UserServiceIml());

        //協(xié)議 二進(jìn)制壓縮
        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();



    }

}


java客戶端 client

public class Client {
    public static void main(String ...args)  {
        TTransport transport = new TFastFramedTransport(new TSocket("localhost",7788),600);

        TCompactProtocol protocol = new TCompactProtocol(transport);

        UserService.Client client = new UserService.Client(protocol);

        try {
            transport.open();
            UserRequest request = new UserRequest();
            request.setUsername("li si");
            request.setPassworld("****");
            User user = client.login(request);
            System.out.println("--------------------------------");
            System.out.println(user);
            System.out.println("--------------------------------");

            List<User> list = client.userList();

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


        } catch (Exception ex){
            ex.printStackTrace();
        } finally {
            transport.close();
        }


    }
}

 

php客戶端

  • 生成php代碼
thrift -r --gen php src/thrift/users.thrift

UserClient.php

header("Content-Type: text/html; charset=UTF-8");
ini_set("display_errors", 'On');
error_reporting(E_ALL);
require_once __DIR__ . '/lib/Thrift/ClassLoader/ThriftClassLoader.php';

use Thrift\ClassLoader\ThriftClassLoader;
use \Thrift\Transport\TSocket;
use \Thrift\Transport\TFramedTransport;
use \Thrift\Protocol\TCompactProtocol;
use \thrift\UserServiceClient;

$GEN_DIR = __DIR__ . '/gen-php';

$loader = new ThriftClassLoader();
$loader->registerNamespace('Thrift', __DIR__ . '/lib');
$loader->registerDefinition('thrift', $GEN_DIR);
$loader->register();


$socket = new TSocket("localhost", 7788);

$transport = new TFramedTransport($socket);
$protoc = new TCompactProtocol($transport);

$client = new UserServiceClient($protoc);
echo 1;
try {
    $transport->open();
    echo 2;
    $request = new \thrift\UserRequest();
    $request->username = "張三";
    $user = $client->login($request);
    print_r($user);
    echo 3;
    $list = $client->userList();
    echo $list[0]->username."ok";




} catch (\tutorial\DataException $ex) {
    echo 'error';
    print 'TException: ' . $ex->getMessage() . "\n";

}
$transport->close();


代碼步驟

thfit服務(wù)器端代碼
  1. 創(chuàng)建Handler,用于處理業(yè)務(wù)邏輯,數(shù)據(jù)處理接口.

  2. 基于Handler創(chuàng)建Processor,數(shù)據(jù)處理對象

  3. 創(chuàng)建Transport(通信方式),數(shù)據(jù)傳輸方式

  4. 創(chuàng)建Protocol方式(設(shè)定傳輸格式),數(shù)據(jù)傳輸協(xié)議

  5. 基于Processor, Transport和Protocol創(chuàng)建Server

  6. 運(yùn)行Server

thrift客戶端:
  1. 創(chuàng)建Transport

  2. 創(chuàng)建Protocol方式

  3. 基于Transport和Protocol創(chuàng)建Client

  4. 運(yùn)行Client的方法

Thrift 深入了解

Thrift 架構(gòu)圖

client
表示與服務(wù)端連接的那個(gè)對象,進(jìn)行方法調(diào)用,
write/read
    thift 幫助我們自動(dòng)生成的, write 是將客戶端的數(shù)據(jù)寫到socket(服務(wù)器端),read 從socket讀到客戶端  
TProtocal
應(yīng)用層 表示協(xié)議 數(shù)據(jù)格式 比如json
TTransport
傳輸層 
TCompactProtocol 即使使用charlse 抓包 拿到數(shù)據(jù)也是二進(jìn)制

Thrift 傳輸格式Protocol

  • TBinaryProtocol 二進(jìn)制格式

  • TCompactProtocol 壓縮二進(jìn)制格式(常用的方式)

  • TJSONProtocol JSON格式

  • TSimpleJSONProtocal 提供json只寫協(xié)議,生成的文件很容易通過腳本語言解析。

  • TDebugProtocal 使用易懂的可讀的文本格式,以便于debug

Thrift 傳輸方式Transport

  • TSocket 阻塞式socket

  • TFramedTransport 以frame 為單位進(jìn)行傳輸,非阻塞式服務(wù)中使用(常用的方式)

  • TFileTransport 以文件形式進(jìn)行傳輸

  • TMemoryTransport 將內(nèi)存用于I/O , java 實(shí)現(xiàn)時(shí)內(nèi)部實(shí)際使用了簡單的ByteArrayOutputStream。

  • TZlibTransport 使用zlib進(jìn)行壓縮,與其他傳輸方式聯(lián)合使用。當(dāng)前無java實(shí)現(xiàn)。

Thrift 支持的服務(wù)模型

  • TSimpleServer 簡單的單線程服務(wù)模型,常用于測試

  • TThreadPoolServer 多線程服務(wù)模型,使用標(biāo)準(zhǔn)的阻塞式IO

  • TNonbolockingServer - 多線程服務(wù)模型,使用非阻塞式IO(需使用TFramedTransport數(shù)據(jù)傳輸方式)

  • THsHaServer - THsHa 引入了線程池去處理,其模型把讀寫任務(wù)放倒線程池去處理;
    Half-sync/Half-async的處理模式,Half-async是在處理IO事件上(accept/read/write io),
    Half-sync用于handler對rpc同步處理(常用方式)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容