RPC是一种C/S架构的服务模型,server端提供接口供client调用,client端向server端发送数据,server端接收client端的数据进行相关计算并将结果返回给client端。
传输效率:
RPC,使用自定义的TCP协议,可以让请求报文体积更小,或者使用HTTP2协议,也可以很好的减少报文的体积,提高传输效率
HTTP,如果是基于HTTP1.1的协议,请求中会包含很多无用的内容,如果是基于HTTP2.0,那么简单的封装以下是可以作为一个RPC来使用的,这时标准RPC框架更多的是服务治理
性能消耗,主要在于序列化和反序列化的耗时
RPC,可以基于thrift实现高效的二进制传输
HTTP,大部分是通过json来实现的,字节大小和序列化耗时都比thrift要更消耗性能
Thrift通过一个中间语言(IDL, 接口定义语言)来定义RPC的接口和数据类型,然后通过一个编译器生成不同语言的代码,并由生成的代码负责RPC协议层和传输层的实现。
Thrift实际上是实现了C/S模式,通过代码生成工具将接口定义文件生成服务器端和客户端代码(可以为不同语言),从而实现服务端和客户端跨语言的支持。用户在Thirft描述文件中声明自己的服务,这些服务经过编译后会生成相应语言的代码文件,然后用户实现服务(客户端调用服务,服务器端提服务)便可以了。其中protocol(协议层, 定义数据传输格式,可以为二进制或者XML等)和transport(传输层,定义数据传输方式,可以为TCP/IP传输,内存共享或者文件共享等)被用作运行时库。
1.在Client和Server的最顶层都是用户自定义的处理逻辑,也就是说用户只需要编写用户逻辑,就可以完成整套的RPC调用流程。用户逻辑的下一层是Thrift自动生成的代码,这些代码主要用于结构化数据的解析,发送和接收,同时服务器端的自动生成代码中还包含了RPC请求的转发
2.TTransport负责以字节流方式发送和接收Message,是底层IO模块在Thrift框架中的实现,每一个底层IO模块都会有一个对应TTransport来负责Thrift的字节流(Byte Stream)数据在该IO模块上的传输。例如TSocket对应Socket传输,TFileTransport对应文件传输。
3.TProtocol主要负责结构化数据组装成Message,或者从Message结构中读出结构化数据。TProtocol将一个有类型的数据转化为字节流以交给TTransport进行传输,或者从TTransport中读取一定长度的字节数据转化为特定类型的数据。
4.TServer负责接收Client的请求,并将请求转发到Processor进行处理。TServer主要任务就是高效的接受Client的请求,特别是在高并发请求的情况下快速完成请求。
5.Processor(或者TProcessor)负责对Client的请求做出响应,包括RPC请求转发,调用参数解析和用户逻辑调用,返回值写回等处理步骤。Processor是服务器端从Thrift框架转入用户逻辑的关键流程。Processor同时也负责向Message结构中写入数据或者读出数据。
利用Thrift用户只需要做三件事:
1) 利用IDL定义数据结构及服务
2) 利用代码生成工具将(1)中的IDL编译成对应语言(如PYTHON),编译后得到基本的框架代码
3) 在(2)中框架代码基础上完成完整代码
centos环境安装thrift
1.下载boost https://www.boost.org/
./bootstrap.sh
./b2 install
2.下载thrift http://thrift.apache.org/download
./configure
make
make install
使用thrift,python示例
1.编写IDL
test_thrift.thrift
样例:
typedef i32 int
struct pair {
1: string str1,
2: string str2
}
service test_thrift
{
int test_api(
1:int n1,
2:int n2)
int test_pair(
1: pair n1
)
pair test_pair2(
1: int n1
)
}
2.编译:thrift -gen py test_thrift.thrift
会在同目录下生成 gen-py 目录,目录下test_thrift文件夹为编译好的调用库
-out可将gen-py替换为指定目录,需要先建立好目录
例:thrift -gen py -out thrift_gen eslib_rpc.thrift
3.服务器端样例代码:
server.py:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from test_thrift import test_thrift
from test_thrift.ttypes import *
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer
class RpcServerHandler(object):
def test_api(self, n1, n2):
print 'test_api'
print n1, n2
return n1 + n2
def test_pair(self, n1):
print 'test_pair'
print n1
return int(n1.str1)
def test_pair2(self, n1):
print 'test_pair2'
print n1
result = pair('100', '200')
print result
return result
if __name__ == '__main__':
handler = RpcServerHandler()
processor = test_thrift.Processor(handler)
transport = TSocket.TServerSocket(port=9080)
tfactory = TTransport.TBufferedTransportFactory()
pfactory1 = TBinaryProtocol.TBinaryProtocolFactory()
server_bin = TServer.TSimpleServer(processor, transport, tfactory, pfactory1)
print('Starting the server...')
server_bin.serve()
print('done.')
4.客户端样例代码
#!/usr/bin/env python
# encoding: utf-8
from thrift.transport import TTransport
from thrift.transport import TSocket
from thrift.protocol import TBinaryProtocol
from test_thrift import test_thrift
from test_thrift.ttypes import pair
def rpc_client():
transport = TSocket.TSocket("127.0.0.1", 9080)
transport = TTransport.TBufferedTransport(transport)
protocol = TBinaryProtocol.TBinaryProtocol(transport)
client = test_thrift.Client(protocol)
transport.open()
return transport, client
transport, client = rpc_client()
result = client.test_api(1, 2)
print result
new_pair = pair('10', '20')
result = client.test_pair(new_pair)
print result
result = client.test_pair2(1)
print result
transport.close()
主要任务是接收Client的请求,并转到某个TProcessor上进行请求处理,针对不同的访问规模,Thrift提供了不同的TServer模型:
1. TSimpleServer:使用阻塞IO的单线程服务器
2. TThreadedServer:使用阻塞IO的多线程服务器,每一个请求都在一个线程里处理,并发访问情况下会有很多线程同时在运行
3. TThreadPoolServer:使用阻塞IO的多线程服务器,使用线程池处理线程
4. TNonBlockingServer: 使用非阻塞IO的多线程服务器,使用少量线程即可完成大并发量的请求响应,比如使用TFramedTransport
Thrift使用livevent作为服务的事件驱动器,libevent就是epoll更高级的封装,处理大量请求的话,主要在TThreadedServer与TNonblockingServer中进行选择。TNonblockingServer能够使用少量线程处理大量并发连接,但是延迟较高,TThreadedServer的延迟较低。实际中,TThreadedServer的吞吐量可能会比TNonblockingServer高,但是TThreadedServer的CPU占用要高很多
Thrift最底层的传出可以使用socket,File,Zip来实现,TTransport是与底层数据传输紧密相关的传输层,每一种支持的底层传输方式都存在对应的TTransport。在TTransport这一层,数据按字节流方式处理,即传输层看到的是一个个字节,并把这些字节按照顺序发送和接收。TTransport并不了解它传输的数据是什么类型,实际上传输层也不关心,只需要按照字节方式对数据进行发送和接收即可。数据类型的解析在TProtocol这一层完成。
TTransport有以下几个类:
1. TSocket: 使用阻塞的TCP Socket进行数据传输
2. THttpTransport:采用Http传输协议进行数据传输
3. TFileTransport:文件、日志传输类,允许client将文件传给server,允许server将收到的数据写到文件中
4.TZlibTransport:与其他的TTransport配合使用,压缩后对数据进行传输,或者将收到的数据解压
主要任务是把TTransport中的字节流转化成数据流,在TProtocol这一层就会出现具有数据类型的数据,如整形,浮点数,字符串,结构体等,TProtocol只会按照指定类型将数据读出和写入,和数据的真正用途,在Thrift自动生成的Server和Client中处理
Thrift可以让用户选客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本text和二进制binnary,为了节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议居多
常用协议如下:
1. TBinaryProtocol:二进制格式
2. TComactProtocol:高效率的、密集的二进制编码格式
3. TJSONProtocol:使用JSON的数据编码协议进行数据传输
4. TSimpleJSONProtocol:提供JSON只写协议,生成的文件很容易通过脚本语言解析
5. TDebugProtocol: 使用易懂的可读文本格式,便于debug
TCompactProtocol 高效的编码方式,使用了类似于ProtocolBuffer的Variable-Length Quantity (VLQ) 编码方式,主要思路是对整数采用可变长度,同时尽量利用没有使用Bit。对于一个int32并不保证一定是4个字节编码,实际中可能是1个字节,也可能是5个字节,但最多是五个字节。TCompactProtocol并不保证一定是最优的,但多数情况下都会比TBinaryProtocol性能要更好。
主要对TServer中的一次请求的InputProtocol和OutputProtcol中写入用户逻辑的返回值
Processor是TServer从Thrift框架转到用户逻辑的关键流程,同时Client所有的RPC调用都会经过TProcessor.process处理并转发
TProcessor对于一次RPC调用的处理过程:
1.TServer接收到RPC请求后,调用TProcessor.process进行处理
2.TProcessor.process首先调用TTransport.readMessageBegin接口,读出RPC调用的名称和RPC调用的类型,如果RPC调用类型是RPC Call,则调用TProcessor.process_fn继续处理,对于未知的RPC调用类型,则抛出异常
3. TProcessor.process_fn根据RPC调用名称到自己的processMap中查找对应的RPC处理函数,对于存在的RPC处理函数,调用处理函数继续进行请求响应,不存在抛异常
4. PRC处理函数调用RPC请求参数的解析类,从TProtocol中读入数据完成参数解析,将参数放到一个Struct中,Thrift会检查参数字段id和类型是否匹配,对于不符合要求的参数都会跳过。这样,RPC接口发生变化之后,旧的处理函数在不做修改的情况下,可以通过跳过不认识的参数,继续提供服务,进而在RPC框架中提供接口的多Version支持
5.完成参数解析后,调用用户逻辑,完成真正的请求响应
6.用户逻辑的返回值使用返回值打包类进行打包,写入TProtocol
ThriftClient跟TProcessor一样,主要操作InputProtocol和OutputProtocol
1. send,将用户的调用参数作为一个整体的Struct写入TProcotol,并发送到TServer
2. send结束后,ThriftClient立刻进入Receive状态等待TServer的响应,对应TServer返回的响应,使用返回值解析类进行返回值解析