bigywater.tech
Django · 130

Gunicorn 与 Django WSGI

Django 上生产,最常见的 WSGI 服务器是 gunicorn。它的默认配置是什么?换 worker 类型、加进程能带来多大变化?timeout 与 max-requests 到底杀掉了什么?这篇起真实的 gunicorn 进程逐项量出来。

默认 1 个 sync worker
20 个 0.3 s 的请求要 6084 ms
630 ms
同样的请求,gthread 2 进程 × 8 线程
SIGTERM
等在途请求完成才退出,之后拒绝新连接

默认值

实测 · 本机执行输出
  gunicorn 26.2.0
  workers = 1 | worker_class = sync | threads = 1 | timeout = 30 s | graceful_timeout = 30 s | keepalive = 2 s | max_requests = 0

worker 配置与并发

实测 · 本机执行输出
  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 数与核数的经验公式本文没有验证。

timeout 杀的是什么

实测 · 本机执行输出
  客户端:状态码 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: term

worker 超过 --timeout 没有响应会被主进程杀掉重启,正在处理的请求随之失败。所以需要长耗时请求时,应该改成后台任务,而不是把 timeout 调得很大。

max-requests

实测 · 本机执行输出
  13 个请求的 pid 序列(相邻去重): ['71435', '71438', '71439'] → 共 3 个不同的 worker 进程

优雅停止

实测 · 本机执行输出
  在途请求(2 s)的结果: 状态码 200 | 从 SIGTERM 到主进程退出 1670 ms
  退出之后还能连上吗: ConnectionRefusedError

部署时给 gunicorn 主进程发 SIGTERM,在途请求有 graceful_timeout 时间完成;本文只验证了「在途请求完成」,没有验证超过 graceful_timeout 时的强杀。

分步演示

gunicorn 的行为
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。gunicorn 26.2.0(当前安装版本);视图里 time.sleep 模拟等待;并发请求由本机线程发出;各配置只测一次
    • 没有验证:gevent / uvicorn worker(见第 131 篇)、--preload 的内存共享、HUP 热重载、CPU 密集型视图、Unix socket、nginx 之后的 keepalive 配合、生产级别的压测
    京ICP备2026067502号-1京公网安备11010502063487号