通過實(shí)例解析Python RPC實(shí)現(xiàn)原理及方法
單線程同步
- 使用socket傳輸數(shù)據(jù)
- 使用json序列化消息體
- struct將消息編碼為二進(jìn)制字節(jié)串,進(jìn)行網(wǎng)絡(luò)傳輸
消息協(xié)議
// 輸入
{
in: "ping",
params: "ireader 0"
}
// 輸出
{
out: "pong",
result: "ireader 0"
}
客戶端 client.py
# coding: utf-8
# client.py
import json
import time
import struct
import socket
def rpc(sock, in_, params):
response = json.dumps({"in": in_, "params": params}) # 請求消息體
length_prefix = struct.pack("I", len(response)) # 請求長度前綴
sock.sendall(length_prefix)
sock.sendall(response)
length_prefix = sock.recv(4) # 響應(yīng)長度前綴
length, = struct.unpack("I", length_prefix)
body = sock.recv(length) # 響應(yīng)消息體
response = json.loads(body)
return response["out"], response["result"] # 返回響應(yīng)類型和結(jié)果
if __name__ == '__main__':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", 8080))
for i in range(10): # 連續(xù)發(fā)送10個rpc請求
out, result = rpc(s, "ping", "ireader %d" % i)
print out, result
time.sleep(1) # 休眠1s,便于觀察
s.close() # 關(guān)閉連接

服務(wù)端 blocking_single.py
# coding: utf8
# blocking_single.py
import json
import struct
import socket
def handle_conn(conn, addr, handlers):
print addr, "comes"
while True: # 循環(huán)讀寫
length_prefix = conn.recv(4) # 請求長度前綴
if not length_prefix: # 連接關(guān)閉了
print addr, "bye"
conn.close()
break # 退出循環(huán),處理下一個連接
length, = struct.unpack("I", length_prefix)
body = conn.recv(length) # 請求消息體
request = json.loads(body)
in_ = request['in']
params = request['params']
print in_, params
handler = handlers[in_] # 查找請求處理器
handler(conn, params) # 處理請求
def loop(sock, handlers):
while True:
conn, addr = sock.accept() # 接收連接
handle_conn(conn, addr, handlers) # 處理連接
def ping(conn, params):
send_result(conn, "pong", params)
def send_result(conn, out, result):
response = json.dumps({"out": out, "result": result}) # 響應(yīng)消息體
length_prefix = struct.pack("I", len(response)) # 響應(yīng)長度前綴
conn.sendall(length_prefix)
conn.sendall(response)
if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 創(chuàng)建一個TCP套接字
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 打開reuse addr選項
sock.bind(("localhost", 8080)) # 綁定端口
sock.listen(1) # 監(jiān)聽客戶端連接
handlers = { # 注冊請求處理器
"ping": ping
}
loop(sock, handlers) # 進(jìn)入服務(wù)循環(huán)

多線程同步
- 使用線程庫thread創(chuàng)建原生線程
- 服務(wù)器可并行處理多個客戶端
服務(wù)端 multithread.py

多進(jìn)程同步
- Python的GIL導(dǎo)致單個進(jìn)程只能占滿一個CPU核心,多線程無法利用多核優(yōu)勢
- os.fork()會生成子進(jìn)程
- 子進(jìn)程退出后,父進(jìn)程需使用waitpid系統(tǒng)調(diào)用收割子進(jìn)程,防止其稱為僵尸資源
- 在子進(jìn)程中關(guān)閉服務(wù)器套接字后,在父進(jìn)程中也要關(guān)閉服務(wù)器套接字
- 因?yàn)檫M(jìn)程fork后,父子進(jìn)程都有自己的套接字引用指向內(nèi)核的同一份套接字對象,套接字引用計數(shù)為2,對套接字進(jìn)程close,即將套接字對象的引用計數(shù)減1
PreForking同步
- 進(jìn)程比線程耗費(fèi)資源,通過PreForking進(jìn)程池模型對服務(wù)器開辟的進(jìn)程數(shù)量進(jìn)行限制,避免服務(wù)器負(fù)載過重
- 如果并行的連接數(shù)量超過了prefork進(jìn)程數(shù)量,后來的客戶端請求將會阻塞
單進(jìn)程異步
- 通過事件輪詢API,查詢相關(guān)套接字是否有響應(yīng)的讀寫事件,有則攜帶事件列表返回,沒有則阻塞
- 拿到讀寫事件后,可對事件相關(guān)的套接字進(jìn)行讀寫操作
- 設(shè)置讀寫緩沖區(qū)
- Nginx/Nodejs/Redis都是基于異步模型
- 異步模型編碼成本高,易出錯,通常在公司業(yè)務(wù)代碼中采用同步模型,僅在講究高并發(fā)高性能的場合才使用異步模型
PreForking異步
Tornado/Nginx采用了多進(jìn)程PreForking異步模型,具有良好的高并發(fā)處理能力

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Linux下Python安裝完成后使用pip命令的詳細(xì)教程
這篇文章主要介紹了Linux下Python安裝完成后使用pip命令的詳細(xì)教程,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2018-11-11
教你利用python實(shí)現(xiàn)企業(yè)微信發(fā)送消息
今天帶大家來練習(xí)python實(shí)戰(zhàn),文中對利用python實(shí)現(xiàn)企業(yè)微信發(fā)送消息作了詳細(xì)的圖文解說及代碼示例,對正在學(xué)習(xí)python的小伙伴很有幫助,需要的朋友可以參考下2021-05-05
關(guān)于Kotlin中SAM轉(zhuǎn)換的那些事
這篇文章主要給大家介紹了關(guān)于Kotlin中SAM轉(zhuǎn)換的那些事,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Python利用itchat對微信中好友數(shù)據(jù)實(shí)現(xiàn)簡單分析的方法
Python 熱度一直很高,我感覺這就是得益于擁有大量的包資源,極大的方便了開發(fā)人員的需求。下面這篇文章主要給大家介紹了關(guān)于Python利用itchat實(shí)現(xiàn)對微信中好友數(shù)據(jù)進(jìn)行簡單分析的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。2017-11-11
python GUI庫圖形界面開發(fā)之PyQt5不規(guī)則窗口實(shí)現(xiàn)與顯示GIF動畫的詳細(xì)方法與實(shí)例
這篇文章主要介紹了python GUI庫圖形界面開發(fā)之PyQt5不規(guī)則窗口與顯示GIF動畫的詳細(xì)方法與實(shí)例,需要的朋友可以參考下2020-03-03
PyTorch中torch.load()的用法和應(yīng)用
torch.load()它用于加載由torch.save()保存的模型或張量,本文主要介紹了PyTorch中torch.load()的用法和應(yīng)用,具有一定的參考價值,感興趣的可以了解一下2024-03-03

