RPC框架 之 Apach thrift

Thrift

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

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

Trift 數據類型(Base Types)

Trift 不支持無符號類型,因爲很多編程語言不存在無符號類型,比如java,php
基礎數據類型
  • byte: 有符號字節。

  • i16: 16位有符號整數。

  • i32:32位有符號整數。

  • i64:64位有符號整數。

  • double: 64位 浮點數。

  • string: 字符串。

    1:optional string username,
    2:optional i32 age,
特殊數據類型
  • binary:一系列未編碼的字節

  • N.B.。

Structs結構體
Thrift structs 定義了公共對象,也就是oop語言中的常說的類,但是他不具有繼承性.結構體可以包含很多字段,字段包含的內容: numeric field IDs, optional default values, 結構體的目的就是將一些數據聚合在一起,方便傳輸管理
struct Person {
    1:optional string username,
    2:optional i32 age,
    3:optional bool married
}
容器類型(Containers)
  • list: 一系列由T類型的數據組成的有序列表,元素可以重複。

  • set: 一系列由T類型的數據組成的無序集合,元素不可重複。

  • map: 一個字典結構,key爲K 類型,value爲V 類型,相當於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,規則與struct一樣,
exception NotFoundException {
}

exception InvalidRequestException {
    1: required string why
}

exception DataException{
 1:optional string message;
 2:optional string callStack,
 3:optional string date
}
服務(Services)
Thrift 定義服務相當於java中創建Interface一樣,創建的service經過代碼生成命令之後就會生成客戶端和服務器端的框架代碼。
  • 一個服務(Services)可以定義多個函數。
    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定義,相當於改了別名,語法:
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 關鍵字,語法:
const FieldType Identifier = ConstValue

const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}
命名空間
Thirft的命名空間相當於java中package的意思,主要目的是組織代碼。thrift使用namespace定義命名空間:
namespace 語言名 路徑
    namespace java thrift.generated
    namespace php tutorial
文件包含
Trift也支持文件包含,相當於php/c/c++中的include,java中的import.使用關鍵字include定義:
include “文件名”

include "paratent.thrift"
註釋
Trifit註釋方式支持shell風格的註釋,支持c/c++ 風格的註釋,即#和//開頭的語句都當做註釋,/**/包含的語句就是註釋。
可選與必選
thrift 提供兩個關鍵字required,option,分別用於表示對應的字段是必填的還是可選的。默認是可選
struct People{
    1: required string name;
    2: optional i32 age;
}

一個完整的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 工作原理

  • 如何實現多語言之間的通信?

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

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

安裝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

實現service 接口方法 UserServiceIml.java

public class UserServiceIml implements UserService.Iface {
    @Override
    public User login(UserRequest userRequst) throws DataException, TException {
        System.out.println("--------服務器: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("--------服務器:logOut--------------");
        System.out.println(userRequst);


    }

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

      List userList =   new ArrayList<>();

        User user =  new User();
        user.setId(2);
       user.setUsername("我會回來的");
        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;
    }
}

服務端 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());

        //協議 二進制壓縮
        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服務器端代碼
  1. 創建Handler,用於處理業務邏輯,數據處理接口.

  2. 基於Handler創建Processor,數據處理對象

  3. 創建Transport(通信方式),數據傳輸方式

  4. 創建Protocol方式(設定傳輸格式),數據傳輸協議

  5. 基於Processor, Transport和Protocol創建Server

  6. 運行Server

thrift客戶端:
  1. 創建Transport

  2. 創建Protocol方式

  3. 基於Transport和Protocol創建Client

  4. 運行Client的方法

Thrift 深入瞭解

Thrift 架構圖

client
表示與服務端連接的那個對象,進行方法調用,
write/read
    thift 幫助我們自動生成的, write 是將客戶端的數據寫到socket(服務器端),read 從socket讀到客戶端  
TProtocal
應用層 表示協議 數據格式 比如json
TTransport
傳輸層 
TCompactProtocol 即使使用charlse 抓包 拿到數據也是二進制

Thrift 傳輸格式Protocol

  • TBinaryProtocol 二進制格式

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

  • TJSONProtocol JSON格式

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

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

Thrift 傳輸方式Transport

  • TSocket 阻塞式socket

  • TFramedTransport 以frame 爲單位進行傳輸,非阻塞式服務中使用(常用的方式)

  • TFileTransport 以文件形式進行傳輸

  • TMemoryTransport 將內存用於I/O , java 實現時內部實際使用了簡單的ByteArrayOutputStream。

  • TZlibTransport 使用zlib進行壓縮,與其他傳輸方式聯合使用。當前無java實現。

Thrift 支持的服務模型

  • TSimpleServer 簡單的單線程服務模型,常用於測試

  • TThreadPoolServer 多線程服務模型,使用標準的阻塞式IO

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章