正确做法是仅用 signal.signal() 设置退出标志,由中间件检查并返回503拒绝新请求,事务完整性依赖 atomic 和 on_commit,而非信号处理。

用 signal.signal() 捕获 SIGTERM 但别在主线程里直接写业务逻辑
Django 默认不处理 SIGTERM,进程收到信号后直接退出,正在执行的数据库事务(比如 TransactionMiddleware 包裹的视图)可能被中断,commit() 没发出去,数据就丢了。
不能把清理逻辑塞进信号回调函数里——它运行在异步信号上下文,没法安全调用 Django ORM、无法访问 request/response、也不能触发中间件。正确做法是:只用信号做“通知”,让主循环或管理线程感知到退出意图。
- 用
signal.signal(signal.SIGTERM, lambda s, f: setattr(shutdown_flag, 'done', True))设置一个全局标记 -
shutdown_flag是个简单对象(比如class Flag: done = False),避免用bool这种不可变类型 - Django 的
runserver是单线程阻塞模型,你得在每次请求处理完、或中间件的process_response里检查这个标记
Django 中间件里检查退出信号并拒绝新请求
靠等最后一个请求自然结束不够稳——用户可能正上传大文件,或者视图里有长耗时任务(比如调用外部 API)。得主动“关门”:拒绝新请求,同时允许已开始的请求跑完。
关键不是拦住所有请求,而是让新连接快速失败,不占用 worker 资源。Gunicorn 或 uWSGI 下这步更重要,但本地 runserver 也建议统一处理。
- 在自定义中间件的
process_request里读shutdown_flag.done - 如果是
True,直接返回HttpResponse(status=503, content=b"Shutting down") - 不要用
raise SystemExit或os._exit(),会跳过 Django 正常响应流程,日志和异常捕获全失效 - 注意:这个中间件必须排在最前面(
MIDDLEWARE列表顶部),否则其他中间件可能先报错
事务完整性靠 atomic + on_commit 配合,不是靠信号处理
很多人误以为“我在 SIGTERM 处理函数里手动 transaction.commit() 就能保数据”,其实不行——事务状态和连接绑定,信号回调里根本拿不到当前事务上下文。
真正起作用的是 Django 自身的事务管理机制:只要视图或函数被 @transaction.atomic 包裹,即使进程被杀,只要 Python 解释器还没退出、数据库连接还活着,Django 就会在函数退出时尝试 commit 或 rollback。但前提是——你没提前关闭连接。
- 确保
CONN_MAX_AGE不设为0(即不复用连接),否则SIGTERM后连接池可能直接丢弃未提交事务 - 对必须落地的操作(如发消息、写日志),用
transaction.on_commit(lambda: send_webhook()),它保证只在 commit 成功后触发 - 避免在
atomic块里做耗时 I/O(如 HTTP 请求),否则延长事务持有时间,增加被中断风险
Gunicorn/uWSGI 环境下要额外配置超时与优雅终止窗口
开发时用 python manage.py runserver 看不出问题,一上生产就出事——因为 Gunicorn 默认收到 SIGTERM 后只等 30 秒就强制 SIGKILL,而你的“等待请求完成”逻辑可能卡在慢查询或第三方调用里。
这时候光靠 Python 层信号处理没用,得配合进程管理器的参数,给够缓冲时间。
- Gunicorn 加
--graceful-timeout 60 --timeout 120:前者是 worker 收到 TERM 后最多活多久,后者是单个请求最长容忍时间 - uWSGI 加
--harakiri 120 --worker-reload-mercy 60,原理类似 - 务必在启动脚本里用
kill -TERM $(cat gunicorn.pid),而不是kill -9,否则绕过所有优雅逻辑 - 监控
gunicorn.access日志末尾是否出现大量503或499,这是“正在关门前最后一批请求”的信号
真正的难点不在怎么写信号处理,而在于理清“谁在什么时候控制哪个资源”:信号只是开关,事务由数据库连接生命周期决定,请求调度由 WSGI 容器控制,三者不咬合,就会漏掉一环。别指望一个 signal.signal() 调用解决所有问题。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











