>

Thrift安装使用完全攻略

- 编辑:金沙国际平台登录 -

Thrift安装使用完全攻略

在公司应用中RPC的利用能够说是十一分的左近,使用该技术可以方便的与种种程序交互而毫无思索其编写制定使用的言语。

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语言

万一你对RPC的定义还不太知道,能够点击这里。

Trift 数据类型(Base Types)

Trift 不支持无符号类型,因为很多编程语言不存在无符号类型,比如java,php

现行反革命市情寒食经有这个用到广泛的RPC框架,比如GRPC,而明日大家要介绍的是平等应用大面积的Apache Thrift。那篇小说将带你平安通过全部坑点,请放心食用。

基本功数据类型
  • byte: 有符号字节。

  • i16: 贰十个人有暗记整数。

  • i32:34位有标记整数。

  • i64:64个人有标记整数。

  • double: 64位 浮点数。

  • string: 字符串。

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

Thrift简介

新鲜数据类型
  • binary:一多重未编码的字节

  • N.B.。

Thrift是Facebook的一个开源项目,后来进来Apache举行孵化。Thrift也是永葆跨语言的,所以它有友好的一套IDL。近来它支持大概全体主流的编制程序语言:C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages。Thrift能够帮助八种音讯格式,除了Thrift私有的二进制编码准则和一种LVQ(类似于TLV新闻格式)的音信格式,还会有健康的JSON格式。Thrift的互连网合同创建在TCP左券基础上,何况扶助阻塞式IO模型和多路IO复用模型。大家就要后文详细讲明Apache Thrift的应用。Thrift也是当前最盛行的RPC框架之一,从互连网上各样品质测量检验情形开,Thrift的品质都是当先的。Thrift的官方网址地址为:

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,

第一是设置golang的库:

异常(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
}
go get git.apache.org/thrift.git/lib/go/thrift/...
服务(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)
    }

为了能编写翻译Thrift IDL,大家还得编写翻译thrift-compiler,首要的坑全在这里。

类型定义(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
}

第一,固然你在用ubuntu,千万别用apt去安装,因为官方源和全数ppa里的compiler版本严重过时了;

常量(const)
thrift 也支持常量定义,使用const 关键字,语法:

const FieldType Identifier = ConstValue

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

附带英特网的单身编写翻译compiler教程不要看,基本都过时了,现在thrift的本子现已是1.0.0-dev,如若按那么些教程编写翻译出来的工具是力所不及和go get安装的库一同使用的,何况十分的大约率你连经常编写翻译都没有办法儿张开,要是不应用go get,那么能够服从那边的办法2进行设置。(方法2里编写翻译compiler的步子请跳过,使用本人在上面要讲的法门)

命名空间
Thirft的命名空间相当于java中package的意思,主要目的是组织代码。thrift使用namespace定义命名空间:
namespace 语言名 路径

    namespace java thrift.generated
    namespace php tutorial

好了下边大家开始设置thrift-compiler,首先是设置正视:

文本包括
Trift也支持文件包含,相当于php/c/c++中的include,java中的import.使用关键字include定义:

include “文件名”

include "paratent.thrift"
sudo apt-get install libboost-dev libboost-test-dev libboost-program-options-dev libboost-filesystem-dev libboost-thread-dev libevent-dev automake libtool flex bison pkg-config g++ libssl-dev
注释
Trifit注释方式支持shell风格的注释,支持c/c++ 风格的注释,即#和//开头的语句都当做注释,/**/包含的语句就是注释。

进而大家clone最新的thrift饭馆,然后编写翻译:

可选与必选
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)
}
git clone https://github.com/apache/thriftcd thrift./bootstrap.sh./configure --without-qt4 --wihout-qt5makesudo make install

Thrift 职业规律

  • 何以兑现多语言之间的通讯?

  • 1,数据传输使用socket(各类语言均援助),数据再以特定的格式发送,接收语言举办深入分析。

  • 2,定义thrift 的文本,由thrift文件(IDL)生成两方语言的接口,model,在转移的model以及接口中会有解码编码的代码。

解释一下,--without-qt*是为着加速编写翻译并幸免报错,因为qt始终要借助moc,很轻便出难题同临时间我们一般也用不到,所以舍去。

安装thrift

brew install thrift

一旦您也没有要求别的语言的改动功效,能够用--without-[name]来来往往除,具体参见./configure --help。

调换代码

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

make也足以改成”make -j N“,N是你cpu可用宗旨数,并行编写翻译加急速度。

java thrift示例

设置好后大家运维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 -version# Thrift version 1.0.0-dev

变动代码

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

这么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;
    }
}

别的,不利用go get而用源码进行安装时,请确定保障thrift-compiler和您安装的库的本子一样,不然会有数不胜数的标题出现。当然,使用go get+自行编写翻译的设置格局不会有那一个主题素材。

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



    }

}

Thrift的使用

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


    }
}

第一大家要编写IDL文件,定义RPC的接口和数据。假设您还面生thrift IDL的语法,能够参见这里,它和c/c++的语法十二分相似,上手起来也较轻易。

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 ThriftClassLoaderThriftClassLoader;
use ThriftTransportTSocket;
use ThriftTransportTFramedTransport;
use ThriftProtocolTCompactProtocol;
use thriftUserServiceClient;

$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 thriftUserRequest();
    $request->username = "张三";
    $user = $client->login($request);
    print_r($user);
    echo 3;
    $list = $client->userList();
    echo $list[0]->username."ok";




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

}
$transport->close();

大家来看八个事例,大家定义三个“compute”模块,在内部定义DivMod和MulRange多少个劳务,并定义一种含有DivMod总结结果的数据类型:

代码步骤

图片 1图片 2

thfit服务器端代码
  1. 始建Handler,用于拍卖事情逻辑,数据处理接口.

  2. 依赖Handler创立Processor,数据管理对象

  3. 创建Transport(通讯格局),数据传输格局

  4. 开创Protocol格局(设定传输格式),数据传输左券

  5. 基于Processor, Transport和Protocol创建Server

  6. 运行Server

namespace go computestruct Result {    1: i64 div;    2: i64 mod;}service DivMod {    Result DoDivMod(1:i64 arg1, 2:i64 arg2);}service MulRange {    string BigRange(1:i64 max)}
thrift客户端:
  1. 创建Transport

  2. 创建Protocol方式

  3. 基于Transport和Protocol创建Client

  4. 运行Client的方法

Thrift IDL

Thrift 深远摸底

接下来大家用thrift-compiler将其编写翻译成golang代码:

Thrift 架构图

thrift -r --gen go compute.thrift
client
表示与服务端连接的那个对象,进行方法调用,

它会在当前目录下生成三个gen-go目录,在里头有个compute目录,那正是大家转移的模块,copy出来放在$GOPATH里。

write/read
    thift 帮助我们自动生成的, write 是将客户端的数据写到socket(服务器端),read 从socket读到客户端  

接下去咱们来贯彻服务端,现在大家只定义了接口,而尚未落到实处它,创建服务端需求如下多少个步骤:

TProtocal
应用层 表示协议 数据格式 比如json
  1. 贯彻服务管理接口,在1.0本子中,接口第二个参数需假使context.Context,用以援助撤销或对RPC调用设置超时。
  2. 制造 Processor,也正是把贯彻的接口注册进RPC服务创设Transport,Transport用于管理网络IO。它是一个interface,能够是TSocket,TServerSocket以及NewTHttpClient*或TTransportFactory.GetTransport重返的靶子。
  3. 始建 Protocol,也正是传输左券,一般选择二进制合同来传输数据,当然也得以采纳json或别的格式。
  4. 成立server,使用前边的Processor,Transport,Protocol以及ip地址来创设服务端。
  5. 运行server。
TTransport
传输层 

*
*上面大家先达成接口:

TCompactProtocol 就算采取charlse 抓包 拿到多少也是二进制
// computeThrift 实现service中定义的方法type divmodThrift struct {}// 每个方法除了定义的返回值之外还要返回一个error,包括定义成void的方法。自定义类型会在名字之后加一条下划线
// 暂时用不到context,所以忽略
func (d *divmodThrift) DoDivMod(_ context.Context, arg1, arg2 int64) (*compute.Result_, error) {    divRes := int64(arg1 / arg2)    modRes := int64(arg1 % arg2)    // 生成的用于生成自定义数据对象的函数    res := compute.NewResult_()    res.Div = divRes    res.Mod = modRes        return res, nil}// 尽量一个struct对应一个servicetype mulrangeThrift struct {}func (m *mulrangeThrift) BigRange(_ context.Context, max int64) (string, error) {    result := new    result.SetString("1", 10)    result = result.MulRange(2, max)        return result.String()}

Thrift 传输格式Protocol

  • TBinaryProtocol 二进制格式

  • TCompactProtocol 压缩二进制格式(常用的不二等秘书籍)

  • TJSONProtocol JSON格式

  • TSimpleJSONProtocal 提供json只写契约,生成的文本很轻巧通过脚本语言深入分析。

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

接口完结之后大家就要赤手空拳并运行服务器了,首先大家先成立Transport:

Thrift 传输格局Transport

  • TSocket 阻塞式socket

  • TFramedTransport 以frame 为单位进行传输,非阻塞式服务中动用(常用的主意)

  • TFileTransport 以文件情势开展传输

  • TMemoryTransport 将内部存款和储蓄器用于I/O , java 完成时内部实际应用了大概的ByteArrayOutputStream。

  • TZlibTransport 使用zlib实行压缩,与其余传输方式联合利用。当前无java实现。

// 创建服务器serverTransport, err := thrift.NewTServerSocket(net.JoinHostPort("127.0.0.1", "9999"))if err != nil {    fmt.Println("Error!", err)    os.Exit(1)}

Thrift 帮助的劳务模型

  • TSimpleServer 简单的单线程服务模型,常用于测验

  • TThreadPoolServer 三四线程服务模型,使用职业的阻塞式IO

  • TNonbolockingServer - 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输格局)

  • THsHaServer - THsHa 引进了线程池去管理,其模型把读写义务放倒线程池去管理;
    Half-sync/哈尔f-async的处理形式,Half-async是在拍卖IO事件上(accept/read/write io),
    哈尔f-sync用于handler对rpc同步管理(常用格局)

然后创制传输公约:

// 创建二进制协议protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory

随着大家把落实的接口注册成Processor:

// 创建Processor,用一个端口处理多个服务divmodProcessor := compute.NewDivModProcessor(new(divmodThrift))mulrangeProcessor := compute.NewMulRangeProcessor(new(mulrangeThrift))    multiProcessor := thrift.NewTMultiplexedProcessor()// 给每个service起一个名字multiProcessor.RegisterProcessor("divmod", divmodProcessor)multiProcessor.RegisterProcessor("mulrange", mulrangeProcessor)

那边我们用TMultiplexedProcessor来兑现二个端口监听多少个服务。

末尾正是开发银行服务器:

// 启动服务器server := thrift.NewTSimpleServer4(multiProcessor, serverTransport, transportFactory, protocolFactory)server.Serve()
// 退出时停止服务器
defer server.Stop()

启航服务器这里也可能有坑点,英特网有成文说利用NewTSimpleServer2能够直接启用二进制格式的数量传输,那是错的,想用二进制或另外格式传输数据,必得明确生成对应的ProtocolFactory并采纳NewTSimpleServer4创立服务器。官方给的事例里就是那样做的,作者也是在被频仍坑了数十次事后才承认了那些难题。

客户端:

要一名不文顾客端,也要依照如下多少个步骤:

  1. 创建 Transport
  2. 创建 Protocol
  3. 基于 Potocol 创建 Client
  4. 开发Transport(不自然要在client创立后才张开,但不能够不在protocol创立后,接口调用前开荒)
  5. 调用接口

下边我们来看代码:

func main() {    // 先建立和服务器的连接的socket,再通过socket建立Transport    socket, err := thrift.NewTSocket(net.JoinHostPort("127.0.0.1", "9999"))    if err != nil {        fmt.Println("Error opening socket:", err)        os.Exit(1)    }    transport := thrift.NewTFramedTransport        // 创建二进制协议    protocol := thrift.NewTBinaryProtocolTransport(transport)    // 打开Transport,与服务器进行连接    if err := transport.Open(); err != nil {        fmt.Fprintln(os.Stderr, "Error opening socket to "+"localhost"+":"+"9999", err)        os.Exit(1)    }    defer transport.Close()        // 接口需要context,以便在长操作时用户可以取消RPC调用    ctx := context.Background()        // 使用divmod服务    divmodProtocol := thrift.NewTMultiplexedProtocol(protocol, "divmod")    // 创建代理客户端,使用TMultiplexedProtocol访问对应的服务    c := thrift.NewTStandardClient(divmodProtocol, divmodProtocol)        client := compute.NewDivModClient    res, err := client.DoDivMod(ctx, 100, 3)    if err != nil {        fmt.Println        os.Exit(1)    }    fmt.Println        // 使用mulrange服务    // 步骤与上面的相同    mulProtocol := thrift.NewTMultiplexedProtocol(protocol, "mulrange")    c = thrift.NewTStandardClient(mulProtocol, mulProtocol)    client2 := compute.NewMulRangeClient    num, err := client2.BigRange(ctx, 100)    if err != nil {        fmt.Println        os.Exit(1)    }    fmt.Println}

本来,当先1/4情状下我们只怕都是用单服务,对于单服务来讲,0.11版本的接口和1.0并未变化,这一个找网络的例子就可以。

go build之后我们独家运营client和server。

上边是出口,计算了100÷3和百分之百3的结果,还会有100的阶乘,客商端调用接口后检查err,未有不当发生就输出结果:

图片 3

如此那般二个扶助多服务的RPC示例就做到了。

倘诺有问号依然提议,接待在数短论长建议。

祝玩得欢愉!

本文由编程发布,转载请注明来源:Thrift安装使用完全攻略