因为一些历史原因需要在不同服务器之间建立rpc通道,来满足不定时、不定量且数据量可能会大的函数需要。在简单对比了之后投向了thrift,虽然网上大佬们说thrift的文档做的很烂,但具体有多烂这个我也不知道,可能基础需求来讲也不重要。
Q: 为什么选择rpc而不是RESTful API的方式?
A:
资源粒度。RPC 就像本地方法调用,RESTful API 每一次添加接口都可能需要额外地组织开放接口的数据,这相当于在应用视图中再写了一次方法调用,而且它还需要维护开发接口的资源粒度、权限等;
流量消耗。RESTful API 在应用层使用 HTTP 协议,哪怕使用轻型、高效、传输效率高的 JSON 也会消耗较大的流量,而 RPC 传输既可以使用 TCP 也可以使用 UDP,而且协议一般使用二制度编码,大大降低了数据的大小,减少流量消耗。
Thrift的网络栈结构:
下面两层的数据传输:
TTransport层
代表thrift的数据传输方式,thrift定义了如下几种常用数据传输方式
TSocket
: 阻塞式socket;TFramedTransport
: 以frame为单位进行传输,非阻塞式服务中使用;TFileTransport
: 以文件形式进行传输;
TProtocol层
代表thrift客户端和服务端之间传输数据的协议,通俗来讲就是客户端和服务端之间传输数据的格式(例如json等),thrift定义了如下几种常见的格式
TBinaryProtocol
: 二进制格式;TCompactProtocol
: 压缩格式;TJSONProtocol
: JSON格式;TSimpleJSONProtocol
: 提供只写的JSON协议;
thrift支持的Server模型
thrift主要支持以下几种服务模型
TSimpleServer
: 简单的单线程服务模型,常用于测试;TThreadPoolServer
: 多线程服务模型,使用标准的阻塞式IO;TNonBlockingServer
: 多线程服务模型,使用非阻塞式IO(需要使用TFramedTransport
数据传输方式);THsHaServer
:THsHa
引入了线程池去处理,其模型读写任务放到线程池去处理,Half-sync/Half-async
处理模式,Half-async
是在处理IO事件上(accept/read/write io),Half-sync
用于handler对rpc的同步处理;
TThreadPoolServer
与TNonBlockingServer
都可以在实际生产中拿来用。在客户端不多的情况下,可以选用TThreadPoolServer,但是要注意TThreadPoolServer的客户端只要不从服务器上断开连接,就会一直占据服务器的一个线程,当服务器线程池所有线程都在被使用时,新到来的客户端将排在队列里等待,直到有客户端断开连接,使服务器端线程池出现空闲线程方可继续被提供服务,所以使用这种模型时,一定要注意客户端不使用时不要长时间连接服务器,如果确实有这种需求,请使用TNonblockingServer。
thrift 接口文件
thrift IDL接口文件不支持无符号的数据类型,因为很多编程语言中不存在无符号类型,thrift支持以下几种基本的数据类型
byte
: 有符号字节i16
: 16位有符号整数i32
: 32位有符号整数i64
: 63位有符号整数double
: 64位浮点数string
: 字符串
此外thrift还支持以下容器类型:
list
: 一系列由T类型的数据组成的有序列表,元素可以重复;set
: 一系列由T类型的数据组成的无序集合,元素不可重复;map
: 一个字典结构,Key为K类型,Value为V类型,相当于java中的HashMap;
具体写的时候可以参照github上thrift给出的解释和示例:
https://github.com/apache/thr...
Window安装thrift
- thrift官网上下载windows exe
- C盘新建C:/Thrift文件夹。将thrift的exe改名为thrift.exe
- 我的电脑-属性-环境变量,加入C:/Thrift到PATH
- cmd中输入thrift -version确认安装成功
Linux安装thrift
环境(阿里云服务器):
Linux version 3.10.0-957.5.1.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) )
因为thrift是用c++编译的,需要安装相应的包(需要root权限)
[root@localhost ~]#yum install automake libtool flex bison pkgconfig gcc-c++ boost-devel libevent-devel zlib-devel python-devel ruby-devel openssl-devel
从apache网站上下载thrift包(这里以0.14.0版本举例,路径什么的可以按每个人的意愿更改)
[root@localhost ~]# cd /root/download/ [root@localhost download]# wget http://mirror.bit.edu.cn/apache/thrift/0.14.0/thrift-0.14.0.tar.gz [root@localhost download]# tar -zxvf thrift-0.14.0.tar.gz [root@localhost download]# ls thrift-0.14.0 thrift-0.14.0.tar.gz
配置、编译、安装thrift
[root@localhost download]# cd thrift-0.14.0 [root@localhost thrift-0.14.0]# ./configure [root@localhost thrift-0.14.0]# make [root@localhost thrift-0.14.0]# make install
python使用
哭了,在网上找例子也太乱了。
1 用TSimpleServer建立测试
- pip install thrift
编写接口文件
新建一个仓库,比如说叫thrift-gogogo,在文件夹下新建一个空文件叫helloworld.thrift.在里面以文本的形式写const string HELLO_YK = "manjie-test" service HelloWorld { void ping(), string sayHello(), string sayMsg(1:string msg) }
第一行是可以在thrift里面定义变量,常量的话最后thrift会编到constants.py中。service则是定义了服务的函数
- 通过thrift生成相应文件
通过terminal在对应仓库下用thrift -r --gen py <thrift文件的path>
生成一个叫gen-py的文件夹 - 按一定结构重新整理仓库
除了helloworld这个文件是自动生成并从gen-py中移出来之外,client和server文件夹都是手动新建的。 编辑server.py文件
import sys from helloworld import HelloWorld from helloworld.ttypes import * from thrift.transport import TSocket from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol from thrift.server import TServer import socket class HelloWorldHandler: def __init__(self): self.log = {} def ping(self): print("server function: ping()") def sayHello(self): print("sayHello()") return "say hello from " + socket.gethostbyname(socket.gethostname()) def sayMsg(self, msg): print("sayMsg(" + msg + ")") return "say " + msg + " from " + socket.gethostbyname(socket.gethostname()) sys.path.append('./gen-py') handler = HelloWorldHandler() processor = HelloWorld.Processor(handler) transport = TSocket.TServerSocket('127.0.0.1', 30303) tfactory = TTransport.TBufferedTransportFactory() pfactory = TBinaryProtocol.TBinaryProtocolFactory() server = TServer.TSimpleServer(processor, transport, tfactory, pfactory) print("Starting python server...") server.serve() print("server side: done!")
编辑client.py
import sys sys.path.append('./gen-py') from helloworld import HelloWorld from helloworld.ttypes import * from helloworld.constants import * from thrift import Thrift from thrift.transport import TSocket from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol try: # Make socket transport = TSocket.TSocket('127.0.0.1', 30303) # Buffering is critical. Raw sockets are very slow transport = TTransport.TBufferedTransport(transport) # Wrap in a protocol protocol = TBinaryProtocol.TBinaryProtocol(transport) # Create a client to use the protocol encoder client = HelloWorld.Client(protocol) # Connect! transport.open() client.ping() print("client: ping()") msg = client.sayHello() print(msg) msg = client.sayMsg(HELLO_YK) print(msg) transport.close() except Thrift.TException as tx: print("%s" % tx.message)
- 先运行server.py, 可以看到如下结果:
- 再运行client.py,可以看到如下结果:
此时再返回看server的运行窗口,会变成
2 用TNonblockingServer实现需求
前1-4步和上面的一致,主要区别在于server和client的代码部分
server部分(只标注和SimpleServer设置不同的地方)
from thrift.server import TNonblockingServer handler = HelloWorldHandler() processor = HelloWorld.Processor(handler) transport = TSocket.TServerSocket('127.0.0.1', 30303) server = TNonblockingServer.TNonblockingServer(processor, transport)
client部分(只标注和SimpleServer设置不同的地方)
transport = TTransport.TFramedTransport(transport)
由于使用了non blocking server 模式,对应的模式必须都改为
TFramedTransport
避坑部分
- 接口IDL文件和server里一定要有一个ping函数!如果没有定义ping函数的话client.py运行后会报unknown function错误
参考链接:
thrift基本原理
thrift的windows安装
参考的thrift example 【1】
参考的thrift example 【2】
cpp的TNonblockingServer的简单用法
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。