发展历程
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编译器自动生成的代码,主要用于结构化数据的解析,发送和接收。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框架。