发展历程


Thrift最初由facebook开发用做系统内各语言之间的RPC通信。



2007年由facebook贡献到apache基金 ,08年5月进入apache孵化器。



目前支持的语言有C++, Java, Python, PHP, Ruby,Erlang, Perl,Haskell, C#, Cocoa, JavaScript,Node.js, Smalltalk, andOcaml,可以在不同语言之间实现C/S RPC调用。






特色



传输数据采用二进制格式,相对XML和JSON体积更小,对于高并发、大数据量和多语言的环境更有优势。



Thrift允许你定义一个简单的定义文件中的数据类型和服务接口,以作为输入文件,编译器生成代码用来方便地生成RPC客户端和服务器通信的无缝跨编程语言。






例子_Python



首先介绍一个简单的 Thrift 实现实例,使大家能够快速直观地了解什么是 Thrift 以及如何使用 Thrift 构建服务。

创建一个简单的服务 Hello。首先根据 Thrift 的语法规范编写脚本文件 Hello.thrift,代码如下:

struct MessageMeta {
    1:i32 mid;
    2:string subject;
    3:i32 lablel0;
    4:string from_where;
    5:string to_where;
    6:i64 modifiedDate;
    7:i64 receivedDate;
    8:i64 sentDate;
}


service Hello{
    string helloString(1:string para, 2:string para2)
    MessageMeta hellowMessage(1:MessageMeta message)
 }



其中定义了一个数据结构 MessageMeta,由八个属性,包括INT32、STRING,INT64等类型。


定义了一个服务Hello,包括两个方法,每个方法都定义了参数列表以及返回值类型,每个参数包括参数序号,参数类型以及参数名。

之后在linux下执行 thrift -gen py Hello.thrift。



可以看到在当前目录下生成了一个新的文件夹gen-py,生成的代码在 gen-py/Hello/里面。包括Hello.py、constants.py、ttypes.py。



我们定义的MessageMeta在ttypes.py中生成了一个Class MessageMeta与之对应。



Hello服务则对应 Hello.py




框架


x

thrift框架java间调用 thrift架构_thrift框架java间调用


在最上层是用户自行实现的业务逻辑代码。    


第二层是由thrift编译器自动生成的代码,主要用于结构化数据的解析,发送和接收。TServer主要任务是高效的接受客户端请求,并将请求转发给Processor处理。Processor负责对客户端的请求做出响应,包括RPC请求转发,调用参数解析和用户逻辑调用,返回值写回等处理。上面例子里面Hello.py 就对应这个部分。


TProtocol以下部分是thirft的传输协议和底层I/O通信。TProtocol是用于数据类型解析的,将结构化数据转化为字节流给TTransport进行传输。TTransport是与底层数据传输密切相关的传输层,负责以字节流方式接收和发送消息体,不关注是什么数据类型。底层IO负责实际的数据传输,包括socket、文件和压缩数据流等。



数据类型


在Hello.thrift 我们看看到i32、i64、string这些就是thrift定义的数据类型,那么下面详细介绍一下thrift的数据类型。



基本数据类型


bool:布尔值,true 或 false,对应 Java 的 boolean
byte:8 位有符号整数,对应 Java 的 byte
i16:16 位有符号整数,对应 Java 的 short
i32:32 位有符号整数,对应 Java 的 int
i64:64 位有符号整数,对应 Java 的 long
double:64 位浮点数,对应 Java 的 double
string:未知编码文本或二进制字符串,对应Java 的 String


 


由于thrift是跨平台、跨语言的为了兼容所有只支持有符号整型。



结构体类型


例子中的MessageMeta就是一个结构体类型


struct:定义公共的对象,类似于C 语言中的结构体定义,在Java 中是一个JavaBean

成员是被正整数编号过的,其中的编号使不能重复的,这个是为了在传输过程中编码使用。

字段会有optional和required之分,但是如果不指定则为无类型–可以不填充该值,但是在序列化传输的时候也会序列化进去,optional是不填充则部序列化,required是必须填充也必须序列化。

同一文件可以定义多个struct,也可以定义在不同的文件,进行include引入。

每个字段可以设置默认值。


容器类型


l list <t>: 元素类型为 t 的有序表,容许元素重复。对应 c++ 的 vector , java 的 ArrayList 或者其他语言的数组。


l set<t>: 元素类型为 t 的无序表,不容许元素重复。对应 c++ 中的 set , java 中的 HashSet,python 中的 set , php 中没有 set ,则转换为 list 类型 了


l map< t,t >: 键类型为 t ,值类型为 t 的 kv 对,键不容许重复。对用 c++ 中的 map, Java 的 HashMap , PHP 对应 array,Python/Ruby 的 dictionary 。


异常类型


exception:异常在语法和功能上类似于结构体,差别是异常使用关键字exception,而且异常是继承每种语言的基础异常类。




服务类型


service:包括service_name 以及service里面包括的方法等。像例子中的service Hello




传输格式


TBinaryProtocol –二进制格式


TCompactProtocol –压缩格式


TJSONProtocol – JSON格式


TSimpleJSONProtocol –提供JSON只写协议,生成的文件很容易通过脚本语言解析。


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


这些具体的数据处理方式可以看一下源码是怎么处理的,其中TCompactProtocol是压缩效率最高的。 我们来看一下对于int类型该协议是怎么处理的:






每8位中有1位tag和7位data组成。一个64位的数字最终有可能是10个字节,32位的则可能是5个字节,但是通常的时候数字并不是每个字节上都有数据,这个时候就使得这种压缩的效率提高了很多。




我们使用TBinaryProtocol在上面的例子基础上做一个简单RPC通信。


首先创建HelloServiceImp.py用来覆盖Hello.py里面Iface,写上自己的业务逻辑,代码如下:


# -*- coding: utf-8 -*-

from Hello import Iface

class HelloServiceImpl(
    Iface,
    ):

    def helloString(self, para, para2):
        print para, para2
        return para + para2

    def hellowMessage(self, message):
        return message

然后编辑HelloServiceServer.py 实现server的代码,代码如下:

# -*- coding: utf-8 -*-

from thrift.transport import TSocket
from thrift.protocol import TBinaryProtocol
from thrift.server import TProcessPoolServer

from Hello import Processor
from HelloServiceImpl import HelloServiceImpl

def service_server():
    transport = TSocket.TServerSocket(port=9090)
    factory = TBinaryProtocol.TBinaryProtocolFactory()
    processor = Processor(HelloServiceImpl())
    server = TProcessPoolServer.TProcessPoolServer(processor, transport)

    print('Start server on port 9090')
    server.serve()

service_server()


python HelloServiceServer.py 就能启动服务。

最后实现客户端代码:


# -*- coding: utf-8 -*-

from thrift.transport import TSocket
from thrift.protocol import TBinaryProtocol

from Hello import Client

def service_client():
    transport = TSocket.TSocket('localhost', 9090)
    transport.open()
    protocol = TBinaryProtocol.TBinaryProtocol(transport)
    client = Client(protocol)
    client.helloString('Hello', 'Thrift')
    transport.close()

service_client()

python  HelloServiceServer.py 就实现了一次c/s调用

如果需要使用其它的协议,只需要把服务器和客户端代码里面的 protocol 替换为相应的协议就可以。



传输方式

TSocket -阻塞式socker THttpTransport–采用Http传输协议进行数据传输 TFramedTransport –以frame为单位进行传输,非阻塞式服务中使用。 TFileTransport –以文件形式进行传输。 TMemoryTransport –将内存用于I/O. java实现时内部实际使用了简单的ByteArrayOutputStream。 TZlibTransport –使用zlib进行压缩,与其他传输方式联合使用。当前无java实现。

服务方式


TSimpleServer –简单的单线程服务模型,常用于测试


TThreadPoolServer –多线程服务模型,使用标准的阻塞式IO。


TNonblockingServer –多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式)


等等





未来发展


Thrift经过这些年的发展,版本迭代,已经能够满足大部分需求,但是好像国内使用的不太多。


2015年Facebook推出了fbthrift, 作为Thrift的一个分支 ,包括新的 C++ 服务器。


Google这两年推出了基于ProtoBuf的gRPC,和Thrift有很多类似的地方,正在不断完善,估计未来会成为一个主流的rpc框架。