Python多进程HTTPServer处理POST请求从multiprocessing队列取值挂起问题
问题原因分析
- 多消费者竞争队列消息:你创建的共享队列同时被主进程的while True循环和HTTP服务的POST处理逻辑两个消费者监听,
pcsc_listener写入的刷卡消息只会被其中一个消费者抢到。如果主进程先拿到了active_card消息,HTTP服务的queue.get()就会一直阻塞等待新的刷卡消息,导致POST请求无限挂起。 - 队列读取默认阻塞:
queue.get()默认是永久阻塞模式,只要没有符合要求的消息就会一直停在这一步,不会往下走返回响应。 - HTTP服务默认单线程:Python内置的
BaseHTTPRequestHandler默认是单线程处理请求,第一个POST请求阻塞之后,第二个请求根本无法被处理,排队超时就会直接报错崩溃。 - 多余的异常抛出:你在POST请求返回响应之后主动
raise(RuntimeWarning(post_data)),这个异常会打断HTTP服务的正常处理流程,也是导致后续请求崩溃的直接原因之一。
修复方案
首先明确核心需求:POST请求触发后,等待用户刷卡,将本次刷卡ID返回给请求端。可以按以下步骤调整:
- 移除主进程对刷卡队列的消费逻辑:如果只是需要打印刷卡日志,直接在
pcsc_listener写入队列后打印即可,避免两个消费者抢消息。如果主进程确实需要消费刷卡消息,可以让pcsc_listener同时写入两个独立的队列,一个给HTTP服务用,一个给主进程用。 - 给队列读取增加超时逻辑,避免请求永久挂起,超时后直接返回提示给前端。
- 使用多线程版本的HTTP服务,支持同时处理多个请求。
- 删除POST逻辑末尾多余的异常抛出语句。
修复后的代码示例
主函数代码
# Main routine def main(): with Manager() as manager: queue = manager.Queue() p1 = Process(target=pcsc_listener, args=(queue,)).start() p2 = Process(target=http_server, args=(queue,)).start() # 移除主进程的队列消费逻辑,如需打印日志放到pcsc_listener中实现 # 若主进程确实需要消费刷卡消息,新增单独的日志队列,pcsc同时往两个队列写 p1.join() p2.join()
HTTP服务代码
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler import json from queue import Empty def http_server(queue): class http_handler(BaseHTTPRequestHandler): def do_GET(self): self.send_error(501) self.end_headers() def do_POST(self): content_length = int(self.headers['Content-Length']) post_data = self.rfile.read(content_length).decode("utf-8") if content_length > 0 else "" post_data = json.loads(post_data) card_id = None try: # 最多等待30秒,超时返回错误 msg = queue.get(timeout=30) if msg[0] == "active_card": card_id = msg[1] print(f"Card scanned {card_id}") except Empty: self.send_response(408) self.send_header('Content-type', 'application/json') self.end_headers() self.wfile.write(json.dumps({"error": "wait card scan timeout"}).encode("utf-8")) return self.send_response(200) self.send_header('Content-type', 'application/json') self.end_headers() # 返回刷卡ID,而不是原POST数据 self.wfile.write(json.dumps({"card_id": card_id}).encode("utf-8")) def log_message(self, format, *args): return server = ThreadingHTTPServer(('0.0.0.0', 8000), http_handler) server.serve_forever()
其他可选实现方案
如果你的需求是每次POST请求返回最新一次的刷卡ID,不需要等待新的刷卡操作,那完全可以用共享变量替代队列:用manager.Value或者manager.dict存储最新的刷卡ID,pcsc_listener负责更新这个共享变量,POST请求直接读取变量返回即可,逻辑更简单,也不会出现阻塞问题。
内容的提问来源于stack exchange,提问作者richardevcom
相关产品推荐
相关产品推荐

