Flask SSE接口部署后不稳定,遇504网关超时问题求助
SSE 部署不稳定问题的解决方案
你的 Flask SSE 端点代码如下:
@app.route('/chatlisten', methods=['GET']) def listen(): from_id, to_id = int(request.args['from_id']), int(request.args['to_id']) def stream(): message = message_queue.listen(from_id, to_id) while True: msg = message.get() yield f"data:{msg}\n\n" return Response(stream(), mimetype='text/event-stream')
一、让 SSE 可靠运行的关键优化
1. 定期发送心跳包
网络层(Nginx、负载均衡)会主动断开长时间无数据的连接,必须每隔一段时间发心跳维持连接:
# 若使用标准库 Queue,导入对应异常类 from queue import Empty def stream(): message = message_queue.listen(from_id, to_id) while True: try: # 30秒超时,没新消息就发心跳 msg = message.get(timeout=30) yield f"data:{msg}\n\n" except Empty: # 空注释帧作为心跳,客户端不会解析该内容 yield ":heartbeat\n\n"
2. 配置正确的响应头
禁止缓存、强制长连接、关闭中间件缓冲:
response = Response(stream(), mimetype='text/event-stream') response.headers['Cache-Control'] = 'no-cache' response.headers['Connection'] = 'keep-alive' response.headers['X-Accel-Buffering'] = 'no' # 告诉Nginx不要缓冲流式响应 return response
3. 确保 Gevent 猴子补丁生效
用 gevent worker 必须打补丁,否则阻塞操作会卡死 worker。在应用入口最顶部添加:
from gevent import monkey monkey.patch_all()
启动 gunicorn 时指定 worker 类并设置长超时:
gunicorn -k gevent -w 4 --timeout 3600 your_app:app
4. 处理客户端断开连接
捕获断开异常,及时清理资源,防止 worker 进程挂起:
from flask import request def stream(): message = message_queue.listen(from_id, to_id) try: while True: try: msg = message.get(timeout=30) yield f"data:{msg}\n\n" except Empty: yield ":heartbeat\n\n" # 检查客户端是否主动断开连接 if request.environ.get('werkzeug.request').is_disconnected: break except GeneratorExit: # 客户端断开后,取消队列监听 message_queue.unlisten(from_id, to_id)
二、关于端口与 504 超时的疑问
- SSE 不需要额外端口:它是基于 HTTP/HTTPS 的长连接协议,和你的 Flask 应用共用同一个端口,无需单独开放端口。
- 504 超时的核心原因:
- 中间件超时:Nginx、CDN 这类中间件默认有连接超时(比如 60 秒),如果 SSE 长时间没数据(没消息也没心跳),会被主动断开并返回 504。
- Gunicorn 超时设置过短:如果 gunicorn 的
--timeout参数设得太小,会把长时间运行的 SSE 连接对应的 worker 进程杀死,导致连接中断。 - Nginx 缓冲未关闭:Nginx 默认会缓冲响应,等整个响应完成再发给客户端,但 SSE 是流式输出,缓冲会导致数据延迟甚至连接断开,必须关闭。
附:Nginx 反向代理配置(若使用 Nginx)
location /chatlisten { proxy_pass http://你的Flask应用地址; proxy_buffering off; # 必须关闭缓冲 proxy_read_timeout 3600; # 超时设长一点,比如1小时 proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off; }
内容的提问来源于stack exchange,提问作者Gunslinger
相关产品推荐
相关产品推荐

