python實現一個簡單RPC框架的示例
本文需要一點Python socket基礎。
回顧RPC
- 客戶端(Client):服務呼叫方。
- 客戶端存根(Client Stub):存放服務端地址資訊,將客戶端的請求引數資料資訊打包成網路訊息,再通過網路傳輸傳送給服務端。
- 服務端存根(Server Stub):接收客戶端傳送過來的請求訊息並進行解包,然後再呼叫本地服務進行處理。
- 服務端(Server):服務的真正提供者。
- Network Service:底層傳輸,可以是 TCP 或 HTTP。
實現jsonrpc
在實現前,簡單理一下整體思路。
1、Network Service 直接使用Python Socket相關的API實現 2.傳輸資料使用JSON,在Socket層會被壓成二進位制,我們無需關心。
模仿xmlrpc,Client與Server都採用Minix多繼承機制來實現,每個類負責自身的事情,最終暴露出現的只有一個類中有限的方法。
先從Client端開始實現。
#client.py importrpcclient c=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方法返回的結果打印出了。
RPCClient類繼承於TCPClient類與RPCStub類。
#rpclient.py classRPCClient(TCPClient,RPCStub): pass
其中TCPClient負責通過Socket實現TCP連結並將資料請求過去,而RPCStub類主要將Client端呼叫Server端方法的相關資訊打包,然後呼叫TCPClient類中的方法傳送則可,兩個類同樣實現在rpclient.py檔案中,程式碼如下。
classTCPClient(object): def__init__(self): self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) defconnect(self,host,port): '''連結Server端''' self.sock.connect((host,port)) defsend(self,data): '''將資料傳送到Server端''' self.sock.send(data) defrecv(self,length): '''接受Server端回傳的資料''' 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)#接收方法執行後返回的結果 returndata setattr(self,function,_func) return_func
TCPClient類就是常規的Socket API的操作,無需多言,主要看看RPCStub類。
當我們在Client端呼叫res = c.add(1,c=3)時,會執行RPCStub中的__getattr__方法,該方法會將Client端呼叫的方法、引數等資訊通過TCPServer類的send方法傳送,傳送資料進行了JSON格式化,方便Server端解碼,隨後便呼叫recv方法等待Server端相應的資料返回。
因為RPCClient類本身沒有add方法,為了讓使用者做到Client端直接呼叫Server端方法的形式,先利用__getattr__構建了_func方法,並將其通過setattr方法設定到RPCClient類中,此時該類就有Server端方法對應的映射了。
呼叫add方法,就呼叫了對應的_func方法,將資料傳送至Server端。
Client端就這樣搞定了,接著來實現Server端,不用緊張,非常簡單。
Server端的使用方式如下。
#server.py importrpcserver defadd(a,b,c=10): sum=a+b+c returnsum s=rpcserver.RPCServer() s.register_function(add)#註冊方法 s.loop(5000)#傳入要監聽的埠
例項化rpcserver.RPCServer類,然後通過register_function方法將想被Client端呼叫的方法傳入,隨後呼叫loop方法,將要監聽的埠傳入,RPCServer類的實現如下。
#rpcserver.py classRPCServer(TCPServer,JSONRPC,RPCStub): def__init__(self): TCPServer.__init__(self) JSONRPC.__init__(self) RPCStub.__init__(self) defloop(self,port): #迴圈監聽5000埠 self.bind_listen(port) print('Serverlisten5000...') whileTrue: self.accept_receive_close() defon_msg(self,data): returnself.call_method(data)
RPCServer繼承自TCPServer、JSONRPC、RPCStub,這些類同樣實現在rpcserver.py檔案中並且給出了詳細的註釋,所以就詳細解釋了。
classTCPServer(object): 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端資訊''' (client_socket,address)=self.sock.accept() msg=client_socket.recv(1024) data=self.on_msg(msg) client_socket.sendall(data)#回傳 client_socket.close() classJSONRPC(object): def__init__(self): self.data=None deffrom_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) data={"res":res} returnjson.dumps(data).encode('utf-8') classRPCStub(object): def__init__(self): self.funs={} defregister_function(self,name=None): '''Server端方法註冊,Client端只可呼叫被註冊的方法''' ifnameisNone: name=function.__name__ self.funs[name]=function
至此,Client端和Server端都寫好了。
測試:
以上就是python實現一個簡單RPC框架的示例的詳細內容,更多關於python 實現RPC框架的資料請關注我們其它相關文章!