uwsgi reload 不会中断请求是因为它采用优雅重启机制:启动新工作进程,旧进程继续处理已有请求直至完成再退出。前提包括启用 --master 或 --enable-threads、nginx 配置 proxy_next_upstream 和足够长的 proxy_read_timeout、django 视图避免阻塞逻辑等。

uWSGI reload 为什么不会中断请求
uWSGI 的 --reload-on-rsync 或 --touch-reload 触发的 reload,并不是杀掉主进程再拉起,而是启动新工作进程、等待旧进程处理完当前请求后优雅退出。关键前提是:旧 worker 不接受新请求,但继续服务已建立的连接(包括长连接、上传中请求等)。
常见误解是“只要 reload 就一定不丢请求”,其实有前提:
- 必须启用
--enable-threads或--master(默认开启),否则无法做优雅退出 - 反向代理(如 Nginx)需配置
proxy_next_upstream error timeout http_502;和足够长的proxy_read_timeout(建议 ≥60s),否则会在 worker 退出中途把连接判为失败 - Django 视图里不能有阻塞式长轮询或手动调用
time.sleep()等无超时逻辑,否则 worker 会卡住无法退出
两种可靠 reload 方式怎么选
生产环境推荐用 --touch-reload,比 --reload-on-rsync 更轻量、更可控;--reload-on-rsync 适合配合 CI/CD 自动同步代码后触发,但依赖 rsync 信号机制,容易因文件系统延迟误判。
实操建议:
- 在 uWSGI 配置里加:
--touch-reload /tmp/uwsgi.reload,然后每次更新后执行touch /tmp/uwsgi.reload - 避免用
--reload-on-rsync ./监控整个项目目录,尤其含media/或日志写入路径时,频繁文件变动会反复 reload - 如果用 systemd 管理 uWSGI,确保
KillSignal=SIGTERM(不是 SIGKILL),否则优雅退出流程被跳过
Django 层要配合做什么
Django 本身不参与 reload 流程,但几个细节会影响平滑性:
- 静态文件不要用 Django 的
django.contrib.staticfiles.views.serve在生产环境提供,它会阻塞 worker —— 必须由 Nginx 直接 serve - 数据库连接池(如使用
django-db-geventpool)需支持连接复用和自动重连;原生 SQLite 不支持多进程共享连接,必须换 PostgreSQL 或 MySQL - 缓存层(Redis/Memcached)连接不应在模块顶层初始化,而应在视图或中间件中按需获取,避免 reload 后旧连接残留失效
- 如果用了 Celery,worker 进程要单独管理,uWSGI reload 不影响它;但注意
task.delay()调用不能卡在未 flush 的数据库事务里
怎么验证 reload 真的没丢请求
别只看 uWSGI 日志里有没有 “Gracefully killing worker”——那只是开始退出,不代表完成。真正要看的是:
- 用
uwsgi --stats :8181开启 stats 接口,访问http://localhost:8181查看workers数组里每个 worker 的last_spawn和is_busy字段,确认旧 workeris_busy降为 0 后才彻底退出 - 模拟长请求:写一个返回
time.sleep(30)的视图,reload 同时 curl 它,观察是否返回 200 而非 502/504 - 检查 Nginx access log,确认 reload 时间点前后没有突增的 502 或超时条目(
upstream prematurely closed connection是典型信号)
最常被忽略的是 Nginx 的 proxy_buffering off; 配置 —— 如果上游响应流式输出且 buffer 关闭,reload 中旧 worker 一旦退出,Nginx 就会立即断开连接,哪怕 Django 已经发出了部分响应。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











