python實(shí)現(xiàn)一個簡單RPC框架的示例
本文需要一點(diǎn)Python socket基礎(chǔ)。
回顧RPC
- 客戶端(Client):服務(wù)調(diào)用方。
- 客戶端存根(Client Stub):存放服務(wù)端地址信息,將客戶端的請求參數(shù)數(shù)據(jù)信息打包成網(wǎng)絡(luò)消息,再通過網(wǎng)絡(luò)傳輸發(fā)送給服務(wù)端。
- 服務(wù)端存根(Server Stub):接收客戶端發(fā)送過來的請求消息并進(jìn)行解包,然后再調(diào)用本地服務(wù)進(jìn)行處理。
- 服務(wù)端(Server):服務(wù)的真正提供者。
- Network Service:底層傳輸,可以是 TCP 或 HTTP。
實(shí)現(xiàn)jsonrpc
在實(shí)現(xiàn)前,簡單理一下整體思路。
1、Network Service 直接使用Python Socket相關(guān)的API實(shí)現(xiàn) 2.傳輸數(shù)據(jù)使用JSON,在Socket層會被壓成二進(jìn)制,我們無需關(guān)心。
模仿xmlrpc,Client與Server都采用Minix多繼承機(jī)制來實(shí)現(xiàn),每個類負(fù)責(zé)自身的事情,最終暴露出現(xiàn)的只有一個類中有限的方法。
先從Client端開始實(shí)現(xiàn)。
# client.py import rpcclient c = rpcclient.RPCClient() c.connect('127.0.0.1', 5000) res = c.add(1, 2, c=3) print(f'res: [{res}]')
實(shí)例化rpcclient.RPCClient類,然后調(diào)用connect方法鏈接Server端,隨后直接調(diào)用Server端的add方法,該方法的效果就是將傳入的數(shù)據(jù)進(jìn)行累加并將累加的結(jié)果返回,最后將add方法返回的結(jié)果打印出了。
RPCClient類繼承于TCPClient類與RPCStub類。
# rpclient.py class RPCClient(TCPClient, RPCStub): pass
其中TCPClient負(fù)責(zé)通過Socket實(shí)現(xiàn)TCP鏈接并將數(shù)據(jù)請求過去,而RPCStub類主要將Client端調(diào)用Server端方法的相關(guān)信息打包,然后調(diào)用TCPClient類中的方法發(fā)送則可,兩個類同樣實(shí)現(xiàn)在rpclient.py文件中,代碼如下。
class TCPClient(object): def __init__(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) def connect(self, host, port): '''鏈接Server端''' self.sock.connect((host, port)) def send(self, data): '''將數(shù)據(jù)發(fā)送到Server端''' self.sock.send(data) def recv(self, length): '''接受Server端回傳的數(shù)據(jù)''' return self.sock.recv(length) class RPCStub(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')) # 發(fā)送數(shù)據(jù) data = self.recv(1024) # 接收方法執(zhí)行后返回的結(jié)果 return data setattr(self, function, _func) return _func
TCPClient類就是常規(guī)的Socket API的操作,無需多言,主要看看RPCStub類。
當(dāng)我們在Client端調(diào)用res = c.add(1, 2, c=3)時,會執(zhí)行RPCStub中的__getattr__方法,該方法會將Client端調(diào)用的方法、參數(shù)等信息通過TCPServer類的send方法發(fā)送,發(fā)送數(shù)據(jù)進(jìn)行了JSON格式化,方便Server端解碼,隨后便調(diào)用recv方法等待Server端相應(yīng)的數(shù)據(jù)返回。
因?yàn)镽PCClient類本身沒有add方法,為了讓用戶做到Client端直接調(diào)用Server端方法的形式,先利用__getattr__構(gòu)建了_func方法,并將其通過setattr方法設(shè)置到RPCClient類中,此時該類就有Server端方法對應(yīng)的映射了。
調(diào)用add方法,就調(diào)用了對應(yīng)的_func方法,將數(shù)據(jù)發(fā)送至Server端。
Client端就這樣搞定了,接著來實(shí)現(xiàn)Server端,不用緊張,非常簡單。
Server端的使用方式如下。
# server.py import rpcserver def add(a, b, c=10): sum = a + b + c return sum s = rpcserver.RPCServer() s.register_function(add) # 注冊方法 s.loop(5000) # 傳入要監(jiān)聽的端口
實(shí)例化rpcserver.RPCServer類,然后通過register_function方法將想被Client端調(diào)用的方法傳入,隨后調(diào)用loop方法,將要監(jiān)聽的端口傳入,RPCServer類的實(shí)現(xiàn)如下。
# rpcserver.py class RPCServer(TCPServer, JSONRPC, RPCStub): def __init__(self): TCPServer.__init__(self) JSONRPC.__init__(self) RPCStub.__init__(self) def loop(self, port): # 循環(huán)監(jiān)聽 5000 端口 self.bind_listen(port) print('Server listen 5000 ...') while True: self.accept_receive_close() def on_msg(self, data): return self.call_method(data)
RPCServer繼承自TCPServer、JSONRPC、RPCStub,這些類同樣實(shí)現(xiàn)在rpcserver.py文件中并且給出了詳細(xì)的注釋,所以就詳細(xì)解釋了。
class TCPServer(object): def __init__(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) def bind_listen(self, port): self.sock.bind(('0.0.0.0', port)) self.sock.listen(5) def accept_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() class JSONRPC(object): def __init__(self): self.data = None def from_data(self, data): '''解析數(shù)據(jù)''' self.data = json.loads(data.decode('utf-8')) def call_method(self, data): '''解析數(shù)據(jù),調(diào)用對應(yīng)的方法變將該方法執(zhí)行結(jié)果返回''' 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} return json.dumps(data).encode('utf-8') class RPCStub(object): def __init__(self): self.funs = {} def register_function(self, function, name=None): '''Server端方法注冊,Client端只可調(diào)用被注冊的方法''' if name is None: name = function.__name__ self.funs[name] = function
至此,Client端和Server端都寫好了。
測試:
以上就是python實(shí)現(xiàn)一個簡單RPC框架的示例的詳細(xì)內(nèi)容,更多關(guān)于python 實(shí)現(xiàn)RPC框架的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python動畫manim中的顏色ManimColor的使用方法詳解
這篇文章主要介紹了python動畫manim中的顏色ManimColor的使用方法,本文通過實(shí)例圖文展示給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-08-08手把手教你快速安裝gpu版本的pytorch(詳細(xì)圖文教程)
在Windows?10上安裝PyTorch時,通常默認(rèn)安裝的是CPU版本,且下載速度較慢,本文提供了一個詳細(xì)的安裝指南,包括如何檢查CUDA版本、選擇合適的PyTorch、torchvision和torchaudio版本,并通過pip而非conda進(jìn)行安裝,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2024-09-09python之如何使用openpyxl設(shè)置單元格樣式
這篇文章主要介紹了python之如何使用openpyxl設(shè)置單元格樣式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06python自動定時任務(wù)schedule庫的使用方法
當(dāng)你需要在 Python 中定期執(zhí)行任務(wù)時,schedule 庫是一個非常實(shí)用的工具,它可以幫助你自動化定時任務(wù),本文給大家介紹了python自動定時任務(wù)schedule庫的使用方法,需要的朋友可以參考下2024-02-02Python爬蟲庫BeautifulSoup的介紹與簡單使用實(shí)例
BeautifulSoup是一個可以從HTML或XML文件中提取數(shù)據(jù)的Python庫,本文為大家介紹下Python爬蟲庫BeautifulSoup的介紹與簡單使用實(shí)例其中包括了,BeautifulSoup解析HTML,BeautifulSoup獲取內(nèi)容,BeautifulSoup節(jié)點(diǎn)操作,BeautifulSoup獲取CSS屬性等實(shí)例2020-01-01Python常用內(nèi)建模塊hashlib、hmac詳解
這篇文章主要介紹了Python常用內(nèi)建模塊hashlib、hmac詳解,摘要算法又稱哈希算法、散列算法,它通過一個函數(shù),把任意長度的數(shù)據(jù)轉(zhuǎn)換為一個長度固定的數(shù)據(jù)串,需要的朋友可以參考下2023-08-08Python空間數(shù)據(jù)處理之GDAL讀寫遙感圖像
這篇文章主要介紹了Python空間數(shù)據(jù)處理之GDAL讀寫遙感圖像,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08