Django 上生产,最常见的 WSGI 服务器是 gunicorn。它的默认配置是什么?换 worker 类型、加进程能带来多大变化?timeout 与 max-requests 到底杀掉了什么?这篇起真实的 gunicorn 进程逐项量出来。
gunicorn 26.2.0 workers = 1 | worker_class = sync | threads = 1 | timeout = 30 s | graceful_timeout = 30 s | keepalive = 2 s | max_requests = 0
sync × 1(默认) 总耗时 6084 ms 用到的 worker 进程数 1 全部 200: True sync × 4 总耗时 1521 ms 用到的 worker 进程数 4 全部 200: True gthread × 1 进程 × 8 线程 总耗时 940 ms 用到的 worker 进程数 1 全部 200: True gthread × 2 进程 × 8 线程 总耗时 630 ms 用到的 worker 进程数 2 全部 200: True
视图里是 time.sleep(模拟等待数据库或外部接口)。如果视图是 CPU 密集的,线程并不会更快(受 GIL 限制,本文没有测)。本机 10 个 CPU 核;worker 数与核数的经验公式本文没有验证。
客户端:状态码 500,用时 2666 ms
之后的下一个请求:状态码 200 (新 worker 已经接手)
gunicorn 日志里相关的行:
[71408] [INFO] Booting worker with pid: 71408
[71407] [CRITICAL] WORKER TIMEOUT (pid:71408)
[71427] [INFO] Booting worker with pid: 71427
[71407] [INFO] Handling signal: termworker 超过 --timeout 没有响应会被主进程杀掉重启,正在处理的请求随之失败。所以需要长耗时请求时,应该改成后台任务,而不是把 timeout 调得很大。
13 个请求的 pid 序列(相邻去重): ['71435', '71438', '71439'] → 共 3 个不同的 worker 进程
在途请求(2 s)的结果: 状态码 200 | 从 SIGTERM 到主进程退出 1670 ms 退出之后还能连上吗: ConnectionRefusedError
部署时给 gunicorn 主进程发 SIGTERM,在途请求有 graceful_timeout 时间完成;本文只验证了「在途请求完成」,没有验证超过 graceful_timeout 时的强杀。