直接 kill -HUP 或 kill -9 会丢连接,因主进程退出导致正在处理的 HTTP 请求(如长轮询、WebSocket、大文件上传)被立即中断;gunicorn 配合 --preload、--graceful-timeout 和 SIGUSR2 才能实现真正平滑重启。

为什么直接 kill -HUP 或重启进程会丢连接
Web 服务(比如用 gunicorn 或 uvicorn 跑的 Flask/FastAPI)收到 SIGHUP 或被 kill -9 杀掉时,主进程退出,所有正在处理的 HTTP 请求会被立即中断——尤其是长轮询、WebSocket、大文件上传或慢查询这类请求,客户端大概率收到 Connection reset 或 502 Bad Gateway。
gunicorn 的 preload + graceful timeout 组合最稳妥
gunicorn 是目前 Python 生态里对平滑重启支持最成熟的选择。关键不是“能不能 reload”,而是“新 worker 启动后、旧 worker 什么时候退场”。
- 必须启用
--preload:否则每个 worker 单独 import 应用,可能因导入顺序/全局状态不一致导致新旧 worker 行为不同 - 设好
--graceful-timeout(比如30):给旧 worker 留出时间完成已有请求,超时后强制杀掉 - 搭配
--timeout(比如120):防止某个请求卡死,拖住整个 graceful 退场过程 - 别用
--reload:开发用可以,生产环境热重载会 fork 新进程但不控制旧进程生命周期,极易丢连接
启动命令示例:
gunicorn --bind :8000 --workers 4 --preload --graceful-timeout 30 --timeout 120 myapp:app
发送 SIGUSR2 触发真正的平滑 reload
不要用 kill -HUP(它只触发配置重载,不重启 worker),正确做法是向 master 进程发 SIGUSR2:
立即学习“Python免费学习笔记(深入)”;
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 先查 master PID:
ps aux | grep gunicorn | grep master - 再执行:
kill -USR2 <master_pid> - 这时 gunicorn 会 fork 出一组新 worker,等新 worker 就绪后,开始让旧 worker 逐个 finish 当前请求并退出
注意:SIGUSR2 不会中断任何连接,但如果你的应用用了全局缓存(如 lru_cache)、单例类或未清理的数据库连接池,新旧 worker 之间状态不共享,需确保这些资源在每次 worker 启动时重新初始化。
FastAPI/Starlette 用户要额外关 WebSocket 生命周期
HTTP 请求靠 gunicorn 的 graceful 机制能兜住,但 WebSocket 是长连接,gunicorn 默认不管理它的生命周期。如果应用里有 websocket 路由,必须手动处理断连重连逻辑:
- 客户端需监听
onclose并实现自动重连(带退避) - 服务端避免在
websockethandler 里依赖进程级状态(比如用内存 dict 存 connection)——改用 Redis 或其他跨进程存储 - 可在
on_connect时记录 client ID 到 Redis,并在on_disconnect清理;新 worker 启动后从 Redis 恢复在线状态(可选)
否则即使 gunicorn 平滑 reload 完成,老 worker 上挂着的 WebSocket 连接仍会被强制关闭,客户端看到 1006 错误。
真正难的不是发信号,而是确认你的应用代码里没有隐式依赖进程生命周期的逻辑——比如缓存没清、DB 连接没 close、后台线程没 join。这些地方一漏,平滑只是假象。

















