长轮询在Flask中禁用time.sleep()因其阻塞worker进程;应选用FastAPI等异步框架或gevent协程模式;需配置Nginx proxy_read_timeout、使用asyncio.wait_for防止无限等待、Redis改用aioredis异步API,并做好超时与事件竞争的双保险处理。

长轮询为什么不能用 time.sleep() 在 Flask 里硬等
因为 Flask 默认是同步阻塞模型,time.sleep() 会卡住整个 worker 进程,一个请求挂起,其他请求就得排队。这不是“挂起”,是“堵死”。真正要的是让单个请求暂停响应,但不占着线程或进程资源。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 必须用异步框架(如
FastAPI+async/await)或支持异步的 WSGI 替代品(如Uvicorn+Starlette),纯 Flask + Gunicorn 默认配置无法安全做长轮询 - 如果非要用 Flask,得配合
gevent或eventlet并启用协程模式,且所有 I/O 操作(比如查 Redis、调下游 API)都得是协程友好的,否则照样阻塞 - 别在视图函数里写
while not event.is_set(): time.sleep(0.5)—— 这是典型错误现象:CPU 空转 + 响应延迟不可控 + 容易超时
asyncio.wait_for() 和 asyncio.Event.wait() 怎么配对用才不丢事件
长轮询本质是“等一个外部信号”,比如消息到达、状态变更。用 asyncio.Event 做信号载体最轻量,但直接 await event.wait() 可能永远不返回——比如事件被 set 后又 reset,或者超时没处理好。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 必须套一层
asyncio.wait_for(event.wait(), timeout=30),否则客户端可能无限等待,后端也没法主动断开 - 捕获
asyncio.TimeoutError后,要显式return JSONResponse({"data": null, "status": "timeout"}),而不是抛异常或忽略 - 事件触发后记得
event.clear(),否则下次请求进来立刻返回,变成“短轮询”;但clear()要在响应发出后、下一次等待前执行,顺序错了就丢事件
客户端发长轮询请求,为什么反复收到 502 Bad Gateway 或连接被重置
不是代码问题,是中间层(Nginx、ALB、Cloudflare)默认把“空闲连接”当异常关掉。它们通常有 60 秒左右的 idle timeout,而长轮询故意要等更久。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- Nginx 配置里必须加:
proxy_read_timeout 90(比你的最大等待时间多 10 秒)、proxy_http_version 1.1、proxy_set_header Connection '' - 如果用 AWS ALB,要在 Target Group 设置里调高
Idle timeout(默认 60,建议设为 90–120) - 客户端 JS 里用
fetch()发请求时,别设signaltimeout 小于后端等待时间,否则前端先放弃,还可能触发重复请求
用 Redis Pub/Sub 做事件源,为什么 redis-py 的 pubsub.listen() 在 async 环境里会卡住
redis-py 默认的 PubSub.listen() 是同步阻塞调用,放进 async 函数里会直接让整个协程停摆。它不兼容 asyncio 事件循环。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 改用
aioredis(v2)或redis-pyv4+ 的asyncAPI:pubsub.subscribe("channel")返回的是协程,必须await - 别在
while True:里循环await pubsub.get_message(ignore_subscribe_messages=True)—— 这会频繁唤醒协程,浪费 CPU;应该用await pubsub.subscribe(...)后监听async for message in pubsub.listen(): - 注意
aioredisv2 不再维护,v3 是纯异步但 API 大改;如果项目已用 v2,升级前先确认所有redis.Redis()实例都换成了redis.Redis.from_url(..., decode_responses=True)再 await
最麻烦的其实是超时与事件竞争:客户端等了 25 秒,第 26 秒消息来了,但 Nginx 在第 30 秒断连,后端还没来得及写响应头——这种边界情况得靠双保险:后端设略短于反向代理的 timeout,客户端也设略短于后端的 fetch timeout,并且每次重连带递增退避。










