05.apache thrift

一、简介

官网:http://thrift.apache.org/

Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的。它通过一个代码生成引擎联合了一个软件栈,来创建不同程度的、无缝的跨平台高效服务,可以使用C#、C++(基于POSIX兼容系统)、Cappuccino、Cocoa、Delphi、Erlang、Go、Haskell、Java、Node.js、OCaml、Perl、PHP、Python、Ruby和Smalltalk。虽然它以前是由Facebook开发的,但它现在是Apache软件基金会的开源项目了。该实现被描述在2007年4月的一篇由Facebook发表的技术论文中,该论文现由Apache掌管

 apache thrift支持语言:C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages.

Thrift本身不但具备消息的格式,并且提供了数据传输的机制,但是也可以使用netty作为底层传输

二、使用场景

企业之间内网系统做分布式应用开发,thrift是首选的,作为两个系统间RPC通信的框架。

三、Thrift工作原理

1、数据传输使用socket,数据以特定格式(字符串/二进制)发送,接收方进行解析

2、定义thrift的IDL文件,生成双方语言的接口、model、解码/编码的代码

四、Thrift的IDL的语法

1、数据类型

thrift不支持无符号类型,因为很多编程语言不支持无符号类型,比如JAVA

1)基本类型

byte有符号字节
i1616位有符号整数
i3232位有符号整数
i6464位有符号整数
double64位浮点数
string字符串

2)容器类型

list一系列由T类型的数据组成的有序列表,元素可以重复
set一系列由T类型的数据组成的无序集合,元素不可以重复
map一个字典结构,key为K类型,value为V类型,相当于Java中的HashMap

1)集合中的元素可以是除了service之外的任何类型,包括exception,其中service指服务端与客户端通信定义的接口

2)定义容器可以使用泛型

3)其他说明

thrift中没有日期类型,通常在thrift的IDL中定义成string类型,再在特定语言中转换成日期类型

2、组件

1)结构体(struct)

将数据聚合在一起,方便传输管理,结构体编译后生成的内容就是:类,(相当于google protobuf的message)

struct People{
    1:string name;
    2:i32 age;
    3:string gender;
}

2)异常(exception)

thrift支持自定义exception,规则与struct一样

exception RequestException{
    1:i32 code;
    2:string reason;
}

3)服务(service)

thrift定义服务相当于java中创建interface一样,创建的service经过代码生成命令之后,就会生成客户端和服务端的框架代码。

service HelloWorldService{

    //service中定义的函数,相当于java interface中定义的方法
    string doAction(1:string name,2:i32 age);
}

4)枚举

枚举的定义形式和java的Enum定义类似:

enum Gender{
    MALE;
    FEMALE;
}

3、语言特性

1)类型定义

thrift支持类似c++一样的typedef定义,作用实际上就是对类型定义一个别名

typedef i32 int
typedef i64 long

上图中i32与i64不太习惯,可以定义成int和long的别名,之后使用int与long标识i32与i64

2)常量

thrift支持常量定义,使用const关键字:

const i32 MAX_RETRIES_TIME = 10
const string MY_WEBSITE = "http://facebook.com"

3)命名空间

Thrift的命名空间相当于java中的package,主要用来设置生成代码的包路径,关键字:namespace

语法:namespace 语言名 路径

namesapce java com.mzj.thrift.demo

4)文件包含

thrift支持文件包含,相当于java中的import。使用关键字include定义,可以定义两个.thrift文件A,B,在A中includeB,这样直接编译A就可以一起生成B的代码

include "global.thrift"

5)注释

thrift注释方式支持shell风格,支持c/c++风格,即:#和//开头,用/**/包裹的都是注释

6)字段必须性

thrift提供两个关键字:必填required、可选optional

struct People{
    1:required string name;
    2:optional i32 age;
}

最佳实践:所有字段使用optional,有业务控制传与不传,定义required字段不便于扩展

五、Thrift的IDL的组成部分

  1. Struct:代表传输的对象类型(Service调用过程中传递的数据类型)
  2. Exception:定义rpc方法调用过程中可能抛出的异常,供服务端再处理请求时如果抛出异常,可以把异常抛给客户端,客户端通过异常内容可以获取异常信息。
  3. Service:相当于java中的接口,Service中定义若干方法,用于在客户端与服务端之间调用使用

六、开发thrift程序

1、搭建开发环境

1)下载并安装thrift编译器

MAC下推荐使用homebrew进行包管理

目前版本是0.12:

  • windows平台可以直接下载官网提供的exe可执行程序,配置path后即可使用:
  • 其他平台(Windows平台也可以)通过源代码进行现编译并使用

通过源码编译方式在特定平台下生成可执行程序这里省略,需要时见官网:

2)工程中引入thrift依赖

在maven中央仓库中搜索:(groupid=org.apache.thrif)org.apache.thrift,进而找到(artifactId=libthrift )libthrift包

https://search.maven.org/

maven:

<dependency>
  <groupId>org.apache.thrift</groupId>
  <artifactId>libthrift</artifactId>
  <version>0.13.0</version>
</dependency>

2、编写IDL文件

namespace java thrift.generated

//---------类型定义(起别名)----------
typedef i16 short
typedef i32 int
typedef i64 long
typedef bool boolean
typedef string String

//---------定义结构体---------
struct Person{
    1: optional String username,//默认是optional,可以不写,但是最好写上
    2: optional int age,
    3: optional boolean married
}

//---------定义异常---------
exception DataException{
    1: optional String message,
    2: optional String callsStack,
    3: optional String date
}

//---------定义service---------
service PersonService{
    Person getPersonByUsername(1:required String username) throws (1: DataException dataException),
    void savePerson(1: required Person person) throws (1: DataException dataException)
}

3、使用thrift编译器将IDL生成对应语言的代码

与protobuf不同的是,protobuf只生成一个源码文件,而thrift生成很多源码文件。

thrift --gen java src/main/resources/thrift/data.thrift

执行完后,会生成如下源码文件:

将生成的源码文件拷贝到对应包路径下:

此步,可以通过git submodule或者git subtree(推荐)进行优化流程:自动化将生成的文件git pull到所依赖的工程

4、编写服务端、客户端、业务实现代码

(新去熟悉一个thrift项目时,需要了解都有哪些结构体、接口等等,此时不要去看thrift自动生成的代码,而是去看.thrift的IDL文件)

实际项目结构体数量:100个左右,service有50个左右

1)服务端代码:ThriftServer.java

package com.mzj.netty.ssy._07_thrift.mycode;

import com.mzj.netty.ssy._07_thrift.PersonService;
import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TTransportException;

/**
 * @Auther: mazhongjia
 * @Description:
 */
public class ThriftServer {
    public static void main(String[] args) throws TTransportException {
        //Socket对象:异步非阻塞服务器
        TNonblockingServerSocket socket = new TNonblockingServerSocket(8899);
        //Server参数
        THsHaServer.Args arg = new THsHaServer.Args(socket).minWorkerThreads(2).maxWorkerThreads(4);
        //骨架 (PersonService.Processor)
        PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl());

        arg.protocolFactory(new TCompactProtocol.Factory());//面向协议层(对应网络7层结构):决定数据如何进行压缩(解码与编码)
        arg.transportFactory(new TFramedTransport.Factory());//面向传输层(对应网络7层结构):决定底层以什么形式将数据由一端传给另一端
        arg.processorFactory(new TProcessorFactory(processor));

        //半同步半异步的Server,(Thrift可以做集群,性能不错)
        TServer server = new THsHaServer(arg);

        //启动服务器,相当于死循环
        server.serve();
    }
}

2)服务实现代码:PersonServiceImpl.java

package com.mzj.netty.ssy._07_thrift.mycode;

import com.mzj.netty.ssy._07_thrift.DataException;
import com.mzj.netty.ssy._07_thrift.Person;
import com.mzj.netty.ssy._07_thrift.PersonService;
import org.apache.thrift.TException;

/**
 * @Auther: mazhongjia
 * @Description:
 */
public class PersonServiceImpl implements PersonService.Iface {
    @Override
    public Person getPersonByUsername(String username) throws DataException, TException {
        System.out.println("getPersonByUsername被调用");
        //处理业务逻辑
        Person person = new Person();
        person.setUsername(username);
        person.setAge(20);
        person.setMarried(false);
        return person;
    }

    @Override
    public void savePerson(Person person) throws DataException, TException {
        System.out.println("savePerson被调用");
        System.out.println(person.getUsername());
        System.out.println(person.getAge());
        System.out.println(person.isMarried());
    }
}

3)客户端代码:ThriftClient.java

package com.mzj.netty.ssy._07_thrift.mycode;

import com.mzj.netty.ssy._07_thrift.Person;
import com.mzj.netty.ssy._07_thrift.PersonService;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

/**
 * @Auther: mazhongjia
 * @Description:
 */
public class ThriftClient {

    public static void main(String[] args) throws TTransportException {
        //客户端transport类型与服务端需要对应——C/S传输方式得一致,600是超时时间
        TTransport transport = new TFramedTransport(new TSocket("localhost", 8899), 600);

        //客户端protocol类型与服务端需要对应——C/S编解码类型得一致
        TProtocol protocol = new TCompactProtocol(transport);//客户端与服务端的Protocol一定要对应,
        //桩 (PersonService.Client)
        PersonService.Client client = new PersonService.Client(protocol);

        try {
            //发起连接
            transport.open();
            //rpc调用
            Person person = client.getPersonByUsername("张山");
            //
            System.out.println(person.getUsername());
            System.out.println(person.getAge());
            System.out.println(person.isMarried());
            System.out.println("------------------");
            client.savePerson(person);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            transport.close();//关闭连接
        }

    }
}

运行服务端,再允许客户端,服务端输出结果为:

客户端输出结果:

5、完整工程示例

https://github.com/mazhongjia/nettyssynetty02/tree/master/src/main/java/com/mzj/netty/ssy/_07_thrift

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值