前言本文将使用Python实现最简单的RPC框架。用xmlrpc清楚。本文需要有一点Pythonsocket基础。如果你对PythonSocket的基础不是很熟悉,推荐阅读RealPython的《SocketProgramminginPython(Guide)》来吐槽VSCode。在开发一些比较复杂的Python项目时,VSCode的debug功能让人感觉很蛋疼,问过windows下用VSCode的同事,没有这个问题。不知道是不是VSCode对Mac的支持有问题。只是我根本不知道怎么用:(本文代码比较简单,所以还是用VSCode开发。那我们开始吧!复习RPC客户端(Client):服务调用者客户端存根(ClientStub):存放服务器的地址信息,将客户端的请求参数数据信息打包成网络报文,然后通过网络传输发送给服务器。服务器存根:接收客户端发送的请求报文,并解包server:真正的服务提供者NetworkService:底层传输,可以是TCP也可以是HTTP在实现jsonrpc之前,先简单了解一下整体思路1.直接NetworkService使用PythonSocket相关的API来实现2.数据传输使用JSON,在Socket层会压缩成二进制,我们不需要关心模仿xmlrpc,Client和Server都是使用Mini实现的x多重继承机制。每个类负责自己的事务,最后只暴露一个类中有限的方法。首先从客户端开始。#client.pyimportrpcclientc=rpcclient.RPCClient()c.connect('127.0.0.1',5000)res=c.add(1,2,c=3)print(f'res:[{res}]')实例化rpcclient.RPCClient类,然后调用connect方法连接server端,然后在server端直接调用add方法。该方法的作用是对传入的数据进行累加,并返回累加的结果,最后打印add方法返回的结果Out。RPCClient类继承自TCPClient类和RPCStub类。#rpclient.pyclassRPCClient(TCPClient,RPCStub):pass其中,TCPClient负责通过Socket实现TCP连接,向过去请求数据,而RPCStub类主要打包客户端调用服务端方法的相关信息,然后调用TCPClient类中的方法send然后,在rpclient.py文件中也实现了这两个类,代码如下。类TCPClient(对象):def__init__(self):self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)defconnect(self,host,port):'''连接到服务器'''self.sock.connect((host,port))defsend(self,data):'''向服务器发送数据'''self.sock.send(data)defrecv(self,length):'''接受服务器的数据终端返回'''returnself.sock.recv(length)classRPCStub(object):def__getattr__(self,function):def_func(*args,**kwargs):d={'method_name':function,'method_args':args,'method_kwargs':kwargs}self.send(json.dumps(d).encode('utf-8'))#发送数据data=self.recv(1024)#接收方法执行后返回返回数据的结果setattr(self,function,_func)return_funcTCPClient类是常规SocketAPI的操作,不用多说,主要看RPCStub类。当我们在客户端调用res=c.add(1,2,c=3)时,会执行RPCStub中的__getattr__方法,通过send传递客户端调用的方法、参数等信息TCPServer类方法发送,发送的数据为JSON格式,方便服务器解码,然后调用recv方法等待服务器返回相应数据。因为RPCClient类本身没有add方法,为了让用户在客户端直接调用服务器端的方法,首先使用__getattr__构造_func方法,通过setattr方法设置给RPCClient类.这个时候类里面就有对应服务端方法的映射。当调用add方法时,调用相应的_func方法将数据发送到服务器。client端就这样搞定了,然后再实现server端,不用紧张,很简单的。服务器端的用法如下。#server.pyimportrpcserverdefadd(a,b,c=10):sum=a+b+creturnsums=rpcserver.RPCServer()s.register_function(add)#注册方法s.loop(5000)#传入监听端口实例化rpcserver.RPCServer类,然后通过register_function方法传入客户端要调用的方法,再调用loop方法传入监听的端口。RPCServer类的实现如下。#rpcserver.pyclassRPCServer(TCPServer,JSONRPC,RPCStub):def__init__(self):TCPServer.__init__(self)JSONRPC.__init__(self)RPCStub.__init__(self)defloop(self,port):#循环监听端口5000self.bind_listen(port)print('Serverlisten5000...')whileTrue:self.accept_receive_close()defon_msg(self,data):returnself.call_method(data)RPCServer继承自TCPServer、JSONRPC、RPCStub,这些该类在rpcserver.py文件中也有实现,并给出了详细的注释,所以解释的很详细。类TCPServer(对象):def__init__(self):self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)defbind_listen(self,port):self.sock.bind(('0.0.0.0',port))self.sock.listen(5)defaccept_receive_close(self):'''获取客户端信息'''(client_socket,address)=self.sock.accept()msg??=client_socket.recv(1024)data=self.on_msg(msg)client_socket.sendall(data)#returnclient_socket.close()classJSONRPC(object):def__init__(self):self.data=Nonedeffrom_data(self,data):'''解析数据'''self.data=json.loads(data.decode('utf-8'))defcall_method(self,data):'''解析数据,调用对应的方法并返回方法的执行结果'''self.from_data(data)method_name=self.data['method_name']method_args=self.data['method_args']method_kwargs=self.data['method_kwargs']res=self.funs[method_name](*method_args,**method_kwargs)数据={"res":res}returnjson.dumps(data).encode('utf-8')classRPCStub(object):def__init__(self):self.funs={}defregister_function(self,function,name=None):'''服务端方法注册,客户端只能调用注册的方法'''ifnameisNone:name=function.__name__self.funs[name]=functionserver-side已经写完了,我们运行一下来总结一下。通过上面的代码,让我们重新认识一下RPC中的这些重要概念,你深入了解了吗?Client(客户端):服务调用者。ClientStub(客户端存根):存储服务端地址信息,将客户端的请求参数数据信息打包成网络报文,然后通过网络传输发送给服务端。服务器存根(ServerStub):接收客户端发送的请求报文并解包,然后调用本地处理服务服务器(Server):服务的真正提供者网络服务:底层传输on,可以是TCP或HTTP。开源的RPC框架肯定没有这么简单,考虑到特殊的边界条件和各种优化。但是RPC本身真的很简单。最后,最近在研究Docker,想通过Go写一个玩具docker,等弄出来再分享Go和docker相关的内容。下篇文章见。对了,如果有帮助,点“在看”或“欣赏”提醒一下。
