gunicorn -k gevent 启动时请求卡死或超时的典型现象是CPU占用低、请求排队、响应延迟飙升,日志频繁出现Worker timeout,根本原因是未执行gevent.monkey.patch_all(),导致标准库I/O仍阻塞;必须在wsgi.py顶部提前patch,并配好数据库连接池与worker参数。

gunicorn -k gevent 启动时请求卡死或超时
典型现象是 CPU 占用很低,但请求排队、响应延迟飙升,gunicorn 日志里频繁出现 Worker timeout 或 Worker failed to boot。根本原因几乎都是漏了 gevent.monkey.patch_all() —— gevent 不是自动协程化的,它必须在任何标准库 I/O 操作(比如 socket、ssl、threading)被导入前,就完成 patch。
正确做法是在 wsgi.py 最顶部插入:
from gevent import monkey monkey.patch_all() <p>import os from django.core.wsgi import get_wsgi_application</p><h1>...其余不变</h1><p>
如果你用 PostgreSQL,还得额外加一行:gevent.monkey.patch_psycopg();MySQL 用户则需 gevent.monkey.patch_pymysql()。不加的话,数据库操作仍会阻塞整个协程。
立即学习“Python免费学习笔记(深入)”;
worker-class 和 worker-connections 怎么配才不浪费也不打满
默认的 -k sync 是同步阻塞模型,100 并发就得 100 个 OS 线程,内存和上下文切换成本高;而 -k gevent 是单进程内协程并发,靠的是 --worker-connections 控制每个 worker 最大能跑多少协程。
-
--workers建议设为$(nproc) * 2 + 1(Linux)或cpu_count() * 2 + 1(Python),别盲目堆到 16+,太多 worker 反而加剧调度竞争 -
--worker-connections默认只有 1000,突发流量下容易打满,可设为2000~4000,但必须同步调高系统文件描述符限制:ulimit -n 65536 - 不要混用
--threads和-k gevent——gthread是多线程模型,gevent是协程模型,二者互斥
数据库连接池没配好,gevent 白搭
gevent 把 Python 层并发提上去了,但如果所有协程共用一个数据库连接,或者连接数远小于 --worker-connections,那实际还是串行等待。Django 默认不带连接池,得靠第三方库补上。
PostgreSQL 推荐 django-db-geventpool,安装后在 settings.py 中替换 DATABASES 的 ENGINE:
DATABASES = {
'default': {
'ENGINE': 'django_db_geventpool.backends.postgresql_psycopg2',
'OPTIONS': {
'MAX_CONNS': 20,
'MIN_CONNS': 4,
},
# ...其余配置
}
}
MySQL 用户可用 django-db-geventpool(适配 PyMySQL)或 django-db-geventpool-mysql。关键是 MAX_CONNS 要 ≥ workers × worker-connections / 10 左右,否则连接池就是瓶颈。
preload 参数为什么不能省
不加 --preload 时,gunicorn 会先 fork 主进程,再让每个 worker 自己执行 import 和 Django 初始化(包括加载 settings、注册 signal、初始化 cache backend)。这会导致:
- 每个 worker 都重复做一遍耗时操作,启动慢
- 多个 worker 同时初始化数据库连接池,可能触发连接数超限
- 某些全局状态(如 Celery app 实例)被复制多份,行为异常
加上 --preload 后,主进程在 fork 前就完成全部 import 和初始化,worker 只继承已就绪的状态。这是生产环境必须打开的开关。
真正难的不是参数组合,而是理解每个参数背后控制的是哪一层资源:OS 进程、Python 线程、gevent 协程、数据库连接、文件描述符——它们之间有隐含依赖,改一个不调另一个,服务照样崩。


















