You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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返回给请求端。可以按以下步骤调整:

  1. 移除主进程对刷卡队列的消费逻辑:如果只是需要打印刷卡日志,直接在pcsc_listener写入队列后打印即可,避免两个消费者抢消息。如果主进程确实需要消费刷卡消息,可以让pcsc_listener同时写入两个独立的队列,一个给HTTP服务用,一个给主进程用。
  2. 给队列读取增加超时逻辑,避免请求永久挂起,超时后直接返回提示给前端。
  3. 使用多线程版本的HTTP服务,支持同时处理多个请求。
  4. 删除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 16:45:02