RPC框架 之 Apach thrift

来源:互联网 发布:巫师3狼派装备数据 编辑:程序博客网 时间:2024/05/18 01:02

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 shorttypedef i32 inttypedef i64 longtypedef bool booleantypedef string String使用struct Person {    1: optional String username,    2: optional int age,    3: optional boolean married}
常量(const)
thrift 也支持常量定义,使用const 关键字,语法:
const FieldType Identifier = ConstValueconst i32 INT32CONSTANT = 9853const 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.generatednamespace php tutorialtypedef i16 shorttypedef i32 inttypedef i64 longtypedef bool booleantypedef string Stringstruct 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.thriftthrift -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.thrift2namespace php thrifttypedef i16 shorttypedef i32 inttypedef i64 longtypedef bool booleantypedef string Stringstruct 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
  • 下载thrift php lib包
    https://github.com/apache/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同步处理(常用方式)

原创粉丝点击