Flask进程收到SIGTERM后丢请求,因开发服务器不处理信号,而Gunicorn等若未配--graceful-timeout和--timeout,会强制终止worker中断正在处理的请求,尤其影响长耗时或流式响应。

Flask进程收到SIGTERM后为什么还会丢请求
因为默认情况下,Flask开发服务器(app.run())不处理系统信号,而生产环境用的WSGI服务器(如Gunicorn、uWSGI)虽能响应SIGTERM,但若未配置优雅停机,会立即终止worker进程,正在处理的请求会被强制中断——尤其是长耗时或流式响应场景。
Gunicorn中启用graceful timeout的关键配置
用Gunicorn部署时,仅靠--timeout不够,它控制单个请求最大执行时间;真正决定“是否等现有请求结束再退出”的是--graceful-timeout和--timeout的配合:
-
--graceful-timeout 30:收到SIGTERM后,最多等待30秒让worker自然完成当前请求 -
--timeout 120:单个请求超时强制杀掉,防止某个卡死请求拖垮整个停机流程 -
--workers 4 --preload:避免--preload缺失导致信号无法广播到所有worker
启动命令示例:gunicorn --bind :8000 --graceful-timeout 30 --timeout 120 --workers 4 --preload app:app
在Flask应用内监听SIGTERM并清理资源
仅靠WSGI服务器不够,业务逻辑里常有数据库连接、缓存客户端、后台线程等需主动关闭。Flask本身无内置信号钩子,需手动注册:
立即学习“Python免费学习笔记(深入)”;
import signal
import sys
from flask import Flask
<p>app = Flask(<strong>name</strong>)
db_conn = None # 示例:全局DB连接</p><p>def cleanup():
if db_conn:
db_conn.close()
print("Cleanup done.")</p><p>def handle_sigterm(signum, frame):
print(f"Received {signum}, shutting down gracefully...")
cleanup()
sys.exit(0)</p><p>signal.signal(signal.SIGTERM, handle_sigterm)</p>注意:sys.exit(0)必须显式调用,否则Gunicorn可能因worker未退出而强行kill,绕过--graceful-timeout。
反向代理(如Nginx)配合健康检查规避502
即使后端已优雅停机,Nginx若在worker退出瞬间转发新请求,仍会返回502 Bad Gateway。解决方式是让Nginx先标记服务为不可用:
- 在停机前,调用一个内部
/health接口返回503,并配Nginxhealth_check或自定义upstream状态管理 - 或使用
nginx -s reload临时移除该server,再发SIGTERM(适合滚动发布) - 避免依赖
proxy_next_upstream error自动重试——它对502重试不保证幂等,可能造成重复提交
最简实践:停机脚本里先curl -X POST http://localhost:8000/shutdown(触发cleanup() + os.kill(os.getpid(), signal.SIGTERM)),再等几秒由Gunicorn收尾。
真正难的是长连接和流式响应——它们占用worker时间远超普通HTTP请求,--graceful-timeout必须设得比最长可能响应时间更宽裕,否则照样被强杀。


















