Python進程池處理并發TCP請求導致客戶端卡死的原因是什么以及如何解決?

python進程池處理并發tcp請求導致客戶端卡死的根本原因及解決方案

本文分析了使用python進程池處理并發TCP請求時,客戶端可能卡死的根本原因,并提供有效的解決方案。問題根源在于服務端代碼使用了multiprocessing.Pool處理每個客戶端連接,但直接將套接字對象傳遞給子進程。由于套接字對象并非進程間共享資源,這種方法會導致子進程無法正確操作套接字,從而造成客戶端卡死。

Python進程池處理并發TCP請求導致客戶端卡死的原因是什么以及如何解決?

原始服務端代碼中,multiprocessing.Pool的apply_async方法直接將clientsocket對象傳遞給子進程的start_request函數。然而,套接字對象與操作系統底層文件描述符綁定,在多進程環境下,子進程無法繼承父進程的套接字文件描述符,導致子進程中的clientsocket無效,無法收發數據。客戶端因此持續等待服務端響應,最終卡死。

解決方案:避免直接傳遞套接字對象。我們可以通過獲取套接字的文件描述符,然后在子進程中使用socket.fromfd函數重新創建套接字對象。這樣,每個子進程擁有獨立的套接字對象,可以正常進行數據收發,避免了資源競爭和復雜的進程間通信。

立即學習Python免費學習筆記(深入)”;

改進后的服務端代碼示例:

import os import socket import sys import time import threading from loguru import logger from concurrent.futures import ThreadPoolExecutor from concurrent.futures._base import Future import multiprocessing  default_encoding: str = 'utf-8'  def init_serversocket() -> socket.socket:     # ... (代碼與原代碼相同) ...  def send_response(clientsocket: socket.socket, addr: tuple, response_body: bytes) -> int:     # ... (代碼與原代碼相同) ...  def start_request(clientsocket_fd: int, addr: tuple) -> int:     clientsocket = socket.fromfd(clientsocket_fd, socket.AF_INET, socket.SOCK_STREAM)     os.close(clientsocket_fd)  # 關閉父進程的文件描述符,防止資源泄露      try:         # ... (代碼與原代碼相同) ...     except Exception as error:         logger.exception(error)     finally:         clientsocket.close()  # 確保在任何情況下都關閉套接字  def worker_process(clientsocket_fd, addr):     start_request(clientsocket_fd, addr)  if __name__ == "__main__":     serversocket = init_serversocket()      pool = multiprocessing.Pool(processes=16)      while True:         try:             clientsocket, addr = serversocket.accept()             clientsocket_fd = clientsocket.fileno()             pool.apply_async(worker_process, (clientsocket_fd, addr))         except Exception as error:             logger.exception(error)      pool.close()     pool.join()

通過以上修改,服務端能夠正確處理并發TCP請求,避免客戶端卡死。關鍵在于將套接字的文件描述符傳遞給子進程,并在子進程中重新創建套接字對象,從而實現進程間的安全通信。

? 版權聲明
THE END
喜歡就支持一下吧
點贊15 分享