Thrift最初由Facebook研发,主要用于各个服务之间的RPC通信,支持跨语言,常用的语言比如C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml都支持。Thrift是一个典型的**CS(客户端/服务端)**结构,客户端和服务端可以使用不同的语言开发。既然客户端和服务端能使用不同的语言开发,那么一定就要有一种中间语言来关联客户端和服务端的语言,没错,这种语言就是IDL(Interface Description Language)
明确要交互的数据格式和具体的方法,定义出thrift接口描述文件(英文叫做IntefaceDescription File)
调用thrift工具,依据thrift接口文件,生成RPC代码;
你的服务器端程序引用thrift生成的RPC代码,并实现其中的Search动作的逻辑,然后启动监听,等待客户端发来请求。
客户端同样引入并调用RPC代码来与服务器端通信
###1. 基本类型
thrift不支持无符号类型,因为很多编程语言不存在无符号类型,比如java
###2. 容器类型
集合中的元素可以是除了service之外的任何类型,包括exception。
###3. 结构体
就像C语言一样,thrift也支持struct类型,目的就是将一些数据聚合在一起,方便传输管理。struct的定 义形式如下:
struct People {
1: required string name;
2: required i32 age = 20;
3: optional string sex;
}
可以看到,结构体中每一个域都有一个正整数标识符,这个标识符并不要求连续,但一旦定义,不建议再进行修改
另外,每个域前都会有required或optional的限定,前者表示是必填域,后者则表示是可选域。域是可以有默认值的,比如上例中的“age"。
如果一个域设置为optional且在构造结构体时没有给这个域赋值,那么在使用这个结构体时,就会忽略掉这个optional的域
thrift支持自定义exception,规则和struct一样,如下:
exception RequestException {
1: i32 code;
2: string reason;
}
除了使用exception来替代struct以外,“异常”这个类型,在语法上和刚才介绍过的结构体的用法是完全一致的。但是从语义上讲,exception和struct却大相径庭。exception是在远程调用发生异常时用来抛出异常用的
服务的定义,与面向对象技术中定义一个接口很类似,而这些接口其实就是纯虚函数。thrift编译工具会根据服务的定义来产生相应的方法和函数。
每个服务,都包括了若干个函数,每个函数包括了若干参数和一个返回值(返回值可以是void.
(小技巧:返回值为void的函数,你可以在函数名前加上oneway标识符,将此函数以异步模式执行,这样在调用此函数后,函数会立即返回。)
对于返回void的函数,thrift仍然会确保函数返回,这样就表示这个函数已被正确执行,且服务器端已有返回信息了。但是如果给void的函数前加上oneway,那么此函数的返回只能表示数据已经进入传输层,并不能表示服务器端已经收到并返回了数据
service vulgar_detect{
bool is_vulgar(1:string title),
i32 calc(1: i32 num)
}
在我们编写好thrift接口描述文件之后,thrift编译工具就要派上用场了,它的作用就是根据thrift接口描述文件来生成相应开发语言的RPC代码.
在终端下输入:
thrift --gen ${开发语言} ${thrift接口描述文件}
关于thrift的更多命令可以输入
thrift --help
说了这么多,我们来看个例子加深印象。
###1. 接口文件demo.thrift
service demo{
string repeat2(1:string msg),
i16 calc(1:i16 num1)
}
###2. 编辑服务端sever.rpy
# coding:utf-8
import sys,os
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer
curPath = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.split(curPath)[0]
sys.path.append(rootPath)
from pygen.demo import demo
class demoHandler:
def __init__(self):
pass
def repeat2(self, msg):
return msg * 2
def calc(self, num1):
return (num1**2)
handler = demoHandler()
processor = demo.Processor(handler)
# 设定主机和端口号
transport = TSocket.TServerSocket(host='localhost',port=30303)
# 选择传输层(和客户端保持一致)
tfactory = TTransport.TBufferedTransportFactory()
# 选择传输协议(和客户端保持一致)
pfactory = TBinaryProtocol.TBinaryProtocolFactory()
# 创建服务端
server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)
print("Starting python server...")
# 开启服务
server.serve()
print("done!")
###3. 编辑客户端
# coding:utf-8
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from pygen.demo import demo
try:
# 设定主机和端口号
transport = TSocket.TSocket('localhost', 30303)
# 选择传输层(和服务端保持一致)
transport = TTransport.TBufferedTransport(transport)
# 选择传输协议(和服务端保持一致)
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# 创建客户端
client = demo.Client(protocol)
# 开启传输
transport.open()
# 发起请求
print(client.repeat2('first thrift demo'))
print(client.calc(72))
# 关闭传输
transport.close()
except Thrift.TException as tx:
print(tx.message)
以上三个文件都在同一目录下
使用如下生产rpc代码,将生成的gen-py重命名为pygen
thrift --gen py demo.thrift
python server.py #开启服务
python client.py # 发起请求