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同步处理(常用方式)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容