2021SC@SDUSC
searpc总结
经过之前的博客,我们分析完了searpc的源码,了解了其客户端与服务器端的工作原理。接下来从以下几个方面对其进行总结。
searpc是一个远程过程调用框架,其作用是使客户端能够通过网络从远程计算机程序上请求服务,而不需要了解底层的网络技术的思想。其具体过程如下:
在客户端方,程序通过向searpc-client
传入函数名以及参数来调用远程函数,其中需要由开发者指定searpc-client
的传输函数(传输函数的作用是向远程服务器发送和调用函数有关的数据、并接受调用后的返回值);而在服务器端,在定义并向searpc-client
注册好rpc函数之后,程序通过监听读出和调用rpc函数有关的数据,并传入到searpc-server
,从而获取到函数调用结果,写回到客户端。
在以上过程中,客户端对函数名与参数的封装、调用传输函数获取到结果、对调用结果的解析以及服务器端存放rpc函数、对接受到参数的解析、找到对应的rpc函数并对结果封装这些过程对使用者来说是透明的。
在上述过程中,服务器存放与寻找rpc函数时,是通过**signature
与marshal
实现的。前者的作用是标识rpc函数;后者的作用是是实现解包、调用、封装过程。与它们的生成相关的过程在程序运行的最初**进行,并最终存生成特定的数据结构与函数,存放到对应的文件中。
同时我们也知道了,由于传输过程由用户定义的特性,我们得以基于不同方式对rpc过程进行实现,如socket
、named-pipe
与异步调用等。
searpc也提供了基于python实现的包,用于在python环境下调用基于c的远程函数。其代码逻辑与基于c的searpc基本相同,故不再赘述。
基于python的searpc实现
在searpc的源码中,还提供了一个基于python的实现的包,可以通过import pysearpc直接使用
服务器端
class SearpcServer(object):
def __init__(self):
self.services = {}
def create_service(self, svcname):
service = SearpcService(svcname)
self.services[svcname] = service
def register_function(self, svcname, fn, fname=None):
service = self.services[svcname]
if fname == None:
fname = fn.__name__
service.func_table[fname] = fn
def _call_function(self, svcname, fcallstr):
"""input str -> output str"""
try:
argv = json.loads(fcallstr)
except Exception as e:
raise SearpcError('bad call str: ' + str(e))
service = self.services[svcname]
fname = argv[0]
fn = service.func_table.get(fname, None)
if fn is None:
raise SearpcError('No such funtion %s' % fname)
ret = fn(*argv[1:])
return ret
def call_function(self, svcname, fcallstr):
try:
retVal = self._call_function(svcname, fcallstr)
except Exception as e:
ret = {'err_code': 555, 'err_msg': str(e)}
else:
ret = {'ret': retVal}
return json.dumps(ret)
服务器端类
客户端
class SearpcClient(object):
def call_remote_func_sync(self, fcall_str):
raise NotImplementedError()
客户端类的父类,在pysearpc-demo-client.py
和named-pipe.py
中均有对其的实现.
之所以对客户端定义基准父类,而服务器端使用可通用的类,是因为客户端的职能主要在于其传输函数,需要程序员对其进行实现;而父类的功能不需要程序员进行实现.
传输过程
class SearpcTransport(object):
"""
A transport is repsonsible to send the serialized request to the
server, and get back the raw response from the server.
"""
def connect(self):
raise NotImplementedError
def send(self, service_name, request_str):
raise NotImplementedError
这个类的作用是用于封装传输函数,无论是基于named-pipe
还是socket
的传输过程,都是通过继承这个类来实现的,以基于named-pipe
的传输函数为例
class NamedPipeTransport(SearpcTransport):
def __init__(self, socket_path):
self.socket_path = socket_path
self.pipe = None
def connect(self):
self.pipe = socket.socket(socket.AF_UNIX)
self.pipe.connect(self.socket_path)
def stop(self):
if self.pipe:
self.pipe.close()
self.pipe = None
def send(self, service, fcall_str):
body = json.dumps({
'service': service,
'request': fcall_str,
})
body_utf8 = body.encode(encoding='utf-8')
# "I" for unsiged int
header = struct.pack('=I', len(body_utf8))
sendall(self.pipe, header)
sendall(self.pipe, body_utf8)
resp_header = recvall(self.pipe, 4)
# logger.info('resp_header is %s', resp_header)
resp_size, = struct.unpack('=I', resp_header)
# logger.info('resp_size is %s', resp_size)
resp = recvall(self.pipe, resp_size)
# logger.info('resp is %s', resp)
return resp.decode(encoding='utf-8')
该传输函数类实现了连接,发送,停止三个操作
然后在此基础上的searpc-client
class NamedPipeClient(SearpcClient):
def __init__(self, socket_path, service_name, pool_size=5):
self.socket_path = socket_path
self.service_name = service_name
self.pool_size = pool_size
self._pool = queue.Queue(pool_size)
def _create_transport(self):
transport = NamedPipeTransport(self.socket_path)
transport.connect()
return transport
def _get_transport(self):
try:
transport = self._pool.get(False)
except:
transport = self._create_transport()
return transport
def _return_transport(self, transport):
try:
self._pool.put(transport, False)
except queue.Full:
transport.stop()
def call_remote_func_sync(self, fcall_str):
transport = self._get_transport()
ret_str = transport.send(self.service_name, fcall_str)
self._return_transport(transport)
return ret_str
可以看到在这个类中基本上对客户端的所有操作进行了封装,相比于c语言实现可读性更强
其功能与原版的searpc完全一致,故不再赘述.