Gunicorn 是一个用 Python 编写的轻量级 WSGI 服务器,遵循 UNIX 风格,兼容多种 Web 框架,如 Django、Flask 等。它通过多进程和多线程方式处理 HTTP 请求,具有简单易用、高效稳定的特点。
主要特点:
安装 Gunicorn 非常简单,可以通过 pip
进行安装。建议在虚拟环境中进行安装,以避免与系统包冲突。
创建并激活虚拟环境(可选)
bashpython3 -m venv venv
source venv/bin/activate
安装 Gunicorn
bashpip install gunicorn
验证安装
bashgunicorn --version
输出示例:
gunicorn (version 20.1.0)
Gunicorn 的基本用法是指定应用的 WSGI 接口。例如,假设你有一个 Flask 应用 app.py
:
python# app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, Gunicorn!"
在终端中运行以下命令:
bashgunicorn app:app
解释:
app
是指 app.py
文件(不需要 .py
后缀)。app
是指 app.py
文件中创建的 Flask 应用实例。默认情况下,Gunicorn 绑定到 127.0.0.1:8000
。可以通过 -b
参数指定:
bashgunicorn -b 0.0.0.0:8000 app:app
默认使用 1 个工作进程。可以通过 -w
参数指定:
bashgunicorn -w 4 app:app
这将启动 4 个工作进程。
使用 -D
参数可在后台运行 Gunicorn:
bashgunicorn -w 4 -D app:app
Gunicorn 提供了丰富的配置选项,可以通过命令行参数或配置文件进行设置。以下是常用的配置选项及其说明。
-w
/ --workers
:指定工作进程数。例如 -w 4
。-b
/ --bind
:绑定的地址和端口。例如 -b 0.0.0.0:8000
。-k
/ --worker-class
:指定工作类型(同步、异步)。例如 -k gevent
。--timeout
:单个worker超时时间,默认为30s,所有请求无输入输出超过此时间将被强制重启。--graceful_timeout
:准备重启时,gunicorn等待服务所有处理完成的最大时间,超过会进行强制关闭worker。--max_requests
:单个worker最大请求数,达到后自动重启--max_requests_jitter
: max_requests 动态范围,gunicorn会自动取范围内随机值动态调整最终max_requests。--log-level
:日志级别(debug, info, warning, error, critical)。--access-logfile
:访问日志文件路径。--error-logfile
:错误日志文件路径。-D
/ --daemon
:后台运行。--reload
:在代码更改时自动重新加载(仅开发环境)。Gunicorn 支持通过配置文件进行设置,配置文件可以是 Python 脚本或配置文件格式。一般推荐使用 Python 配置文件。
示例:gunicorn_conf.py
python# gunicorn_conf.py
"""
gunicorn 配置文件
参考:
https://docs.gunicorn.org/en/stable/settings.html
"""
import os
from pathlib import Path
import multiprocessing
BASE_DIR = Path(__file__).resolve().parents[0]
#================================================
# 基础配置
#================================================
# 绑定ip和端口号
bind = '0.0.0.0:8000'
# 指定Gunicorn处理请求的队列的最大长度,如果监听队列已满,新的请求将被拒绝或丢弃
backlog = 2048
#================================================
# 工作进程配置
#================================================
# 默认sync模式
# wsgi 使用gevent模式可更好解决处理时长较长的请求
# asgi 应用使用uvicorn worker
worker_class = 'gevent'
# worker_class = 'uvicorn.workers.UvicornWorker'
# 进程数,一般填cpu核心数倍数 + 1
workers = (multiprocessing.cpu_count() * 2) + 1
# 指定每个进程开启的线程数
# !warning: asgi 模式下不支持多线程,请直接注释,不要使用
# threads = 2
#================================================
# 自动重启配置
#================================================
# 超时时间,默认为30s,所有请求无输入输出超过此时间将被强制终止
# 保证任务在空闲时仅导出不会被误判杀死
timeout = 660
# 保证任务在计划重启时能被完整执行
graceful_timeout = 660
# 单个worker最大请求数,达到后自动重启
max_requests = 2000
# max_requests 动态范围
max_requests_jitter = 200
#================================================
# 日志配置
#================================================
# 日志文件目录
log_dir = os.path.join(BASE_DIR, "logs", "gunicorn")
os.makedirs(log_dir, exist_ok=True)
# 日志级别,这个日志级别指的是错误日志的级别,而访问日志的级别无法设置
loglevel = 'info'
accesslog = log_dir + "/access.log"
errorlog = log_dir + "/error.log"
# 访问日志格式
# t: 时间, p: 进程ID, h: 远程地址, r: 请求行, s: 状态码,
# L: 请求处理时间(秒), b: 响应长度, f: Referer, a: User-Agent
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s "%(f)s" "%(a)s"'
启动时指定配置文件:
bashgunicorn -c gunicorn_conf.py app:app
配置项 | 类型 | 默认值 | 说明 |
---|---|---|---|
bind | String | 127.0.0.1:8000 | 服务器绑定的地址和端口。 |
workers | Integer | 1 | 工作进程数,建议为 2 x CPU 核数 + 1 。 |
worker_class | String | sync | 工作类型,如 sync , gevent 。 |
threads | Integer | 1 | 每个工作进程的线程数,适用于 gthread 。 |
timeout | Integer | 30 | 请求超时时间(秒)。 |
keepalive | Integer | 2 | HTTP Keep-Alive 超时时间(秒)。 |
backlog | Integer | 2048 | 连接队列长度。 |
preload_app | Boolean | False | 是否在工作进程启动前加载应用。 |
reload | Boolean | False | 是否启用代码更改自动重载(仅开发环境)。 |
reload_engine | String | auto | 指定重载引擎,如 stat , watchman 。 |
reload_extra_files | List | [] | 监控的额外文件列表,文件变动时重载。 |
daemon | Boolean | False | 是否以守护进程模式运行。 |
pidfile | String | None | 保存主进程 PID 文件路径。 |
user | String | 当前用户 | 指定工作进程运行的用户。 |
group | String | 当前组 | 指定工作进程运行的组。 |
umask | Integer | 0 | 文件模式创建掩码。 |
worker_connections | Integer | 1000 | 每个异步工作类型的最大客户端连接数。 |
limit_request_fields | Integer | 327 | 单个请求允许的最大头字段数。 |
limit_request_field_size | Integer | 8190 | 单个头字段的最大字节数。 |
limit_request_line | Integer | 4094 | 请求行的最大字节数。 |
graceful_timeout | Integer | 30 | 优雅关闭时的超时时间(秒)。 |
accesslog | String | - (标准输出) | 访问日志文件路径。 |
errorlog | String | stderr | 错误日志文件路径。 |
loglevel | String | info | 日志记录级别,如 debug , info 。 |
logger_class | String | gunicorn.glogging.Logger | 自定义日志类。 |
access_log_format | String | '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' | 访问日志格式。 |
access_logshow_field | Boolean | True | 是否显示特定日志字段。 |
spew | Boolean | False | 开启详细的调试信息和所有模块导入。 |
disable_redirect_access_to_syslog | Boolean | False | 禁用将访问日志重定向到系统日志。 |
syslog | String/Boolean | False | 启用将日志发送到 syslog。 |
syslog_prefix | String | None | 日志消息的前缀。 |
enable_stdio_inheritance | Boolean | True | 是否继承标准输入/输出文件描述符。 |
graceful_shutdown_timeout | Integer | 30 | 工作进程优雅关闭时的最大等待时间(秒)。 |
raw_env | List | [] | 设置环境变量,格式为 KEY=VALUE 。 |
env_file | String | None | 指定包含环境变量的文件路径。 |
secure_scheme_headers | Dict | {'X-FORWARDED-PROTO': 'https'} | 指定用于安全连接的协议头。 |
proxy_protocol | Boolean | False | 启用 PROXY 协议支持。 |
paste | String | None | 使用 PasteDeploy 的配置文件。 |
worker_tmp_dir | String | /dev/shm | 工作进程的临时目录。 |
proxy_allow_ips | String | 127.0.0.1 | 允许通过反向代理的 IP 地址列表,逗号分隔。 |
ssl_version | String | TLS | 指定 SSL 协议的版本。 |
ssl_certfile | String | None | SSL 证书文件路径。 |
ssl_keyfile | String | None | SSL 密钥文件路径。 |
ssl_ca_certs | String | None | SSL CA 证书文件路径。 |
ssl_ciphers | String | "TLS" | 指定 SSL/TLS 密码套件。 |
ssl_pre_post | Boolean | False | 在 SSL 握手前发送预共享密钥。 |
ssl_keyfile_pass | String | None | SSL 密钥文件的密码。 |
ssl_ocsp | String | None | OCSP 文件路径。 |
fastcgi | Boolean | False | 启用 FastCGI 支持。 |
limit_request | List | [] | 限制特定请求类型的数量。 |
spec | String | None | 使用 spec 文件加载配置。 |
statsd_on | Boolean | False | 启用 StatsD 监控。 |
statsd_host | String | 127.0.0.1 | StatsD 服务器主机地址。 |
statsd_port | Integer | 8125 | StatsD 服务器端口号。 |
statsd_prefix | String | gunicorn | 用于 StatsD 的命名前缀。 |
on_starting | Callable | None | Gunicorn 启动时调用的钩子函数。 |
when_ready | Callable | None | Gunicorn 准备就绪后调用的钩子函数。 |
pre_fork | Callable | None | 每个工作进程分叉前调用的钩子函数。 |
post_fork | Callable | None | 每个工作进程分叉后调用的钩子函数。 |
pre_exec | Callable | None | 在执行 Gunicorn 主程序前调用的钩子函数。 |
pre_request | Callable | None | 每个请求前调用的钩子函数。 |
post_request | Callable | None | 每个请求后调用的钩子函数。 |
child_exit | Callable | None | 工作进程退出时调用的钩子函数。 |
worker_int | Callable | None | 工作进程收到 SIGINT 信号时调用的钩子函数。 |
worker_abort | Callable | None | 工作进程收到 SIGABRT 信号时调用的钩子函数。 |
logconfig | String | None | 指定 Python logging 配置文件路径。 |
logconfig_dict | Dict | None | 通过字典方式配置 logging。 |
enable_soe | Boolean | False | 启用 SOE 模式,用于更好的错误处理。 |
buffer_response | Boolean | False | 是否缓冲响应数据。 |
h11_max_incomplete_event_size | Integer | 4096 | h11 协议中,允许的最大未完成事件大小。 |
enable_h11_parsing | Boolean | False | 启用 h11 协议的解析。 |
default_pidfile | String | None | 默认 PID 文件路径。 |
default_accesslog | String | - (标准输出) | 默认访问日志路径。 |
注意: 以上配置项不是全部,但涵盖了大部分常用和关键的选项。对于更详细和最新的配置选项,请参考 Gunicorn 官方文档。
Gunicorn 能够无缝集成常见的 Python Web 框架,如 Flask 和 Django。
示例 Flask 应用:
python# app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello from Flask!"
启动 Gunicorn:
bashgunicorn -w 4 -b 0.0.0.0:8000 app:app
Django 项目结构示例:
myproject/ manage.py myproject/ __init__.py settings.py urls.py wsgi.py
确保 wsgi.py
存在,通常为默认生成。
启动 Gunicorn:
bashgunicorn myproject.wsgi:application
常用命令示例:
bashgunicorn -w 4 -b 0.0.0.0:8000 myproject.wsgi:application
Gunicorn 提供了丰富的生命周期钩子(Lifecycle Hooks),允许你在服务器和工作进程(worker)生命周期的不同阶段执行自定义代码。这对于管理资源(如数据库连接、消息队列客户端)、设置日志、发送通知等非常有用。
这些钩子通常在 Gunicorn 的配置文件(例如 gunicorn.conf.py
)中定义为函数。
以下是 Gunicorn 提供的一些关键生命周期钩子,以及它们的触发时机和用途:
钩子名称 (Hook Name) | 触发时机 | 执行者 | 常用场景 |
---|---|---|---|
on_starting(server) | 服务器主进程(Master)启动时,在加载任何应用代码之前。 | Master | 设置全局日志、早期配置检查。 |
when_ready(server) | 服务器完全启动并准备好接受连接时。 | Master | 发送“服务已启动”的通知、执行启动后任务。 |
pre_fork(server, worker) | 主进程 fork 出一个新的工作进程之前。 | Master | 准备 fork 所需的资源,例如在 fork 前减少文件描述符。 |
post_fork(server, worker) | 主进程 fork 出一个新的工作进程之后,在子进程中。 | Worker | 非常常用! 为每个工作进程初始化资源,如创建新的数据库连接、Redis 连接池等,避免多进程共享同一个连接。 |
pre_exec(server) | 主进程执行一个新的二进制文件之前(例如,在服务升级或重启时)。 | Master | 保存状态、关闭旧的监听套接字。 |
worker_int(worker) | 工作进程收到 SIGINT 或 SIGQUIT 信号时(通常是优雅地停止)。 | Worker | 执行优雅关闭逻辑,例如等待当前请求完成、关闭连接。 |
worker_abort(worker) | 工作进程由于超时(timeout )而被主进程杀死时。 | Worker | 记录异常信息、发送警报,用于调试僵死的进程。 |
child_exit(server, worker) | 任何一个工作进程退出时(无论正常或异常)。 | Master | 记录工作进程退出事件、更新监控指标。 |
worker_exit(server, worker) | child_exit 的旧称,功能相同。 | Master | 同上。 |
on_exit(server) | Gunicorn 完全关闭时。 | Master | 最终的清理工作。 |
除了服务器和进程级别的钩子,还有请求级别的钩子:
钩子名称 (Hook Name) | 触发时机 |
---|---|
pre_request(worker, req) | 工作进程接收到请求,但在处理请求之前。 |
post_request(worker, req, environ, resp) | 工作进程处理完请求之后,在响应发送给客户端之前。 |
gunicorn.conf.py
配置文件下面是一个包含多个常用钩子的 Gunicorn 配置文件示例,可以很好地展示它们如何工作。
假设你的项目结构如下:
my_project/ ├── my_app.py └── gunicorn.conf.py
my_app.py
(你的 WSGI 应用):
pythondef app(environ, start_response):
data = b"Hello, World!\n"
start_response("200 OK", [
("Content-Type", "text/plain"),
("Content-Length", str(len(data)))
])
return iter([data])
gunicorn.conf.py
(Gunicorn 配置文件):
pythonimport os
# 服务器套接字
bind = "0.0.0.0:8000"
# 工作进程数
workers = 4
# Gunicorn 进程的 PID 文件
pidfile = "/tmp/gunicorn.pid"
# 日志文件
accesslog = "/tmp/gunicorn_access.log"
errorlog = "/tmp/gunicorn_error.log"
print("Gunicorn config file loaded.")
# --- 生命周期钩子 ---
def on_starting(server):
print("1. [Master] on_starting: 服务器正在启动...")
def when_ready(server):
print("2. [Master] when_ready: 服务器已就绪!")
# 可以给监控系统发送一个通知
def pre_fork(server, worker):
worker_id = worker.pid if worker.pid is not None else "N/A"
print(f"3. [Master] pre_fork: 准备 fork worker {worker_id}...")
# 注意:此时 worker.pid 可能是 None,因为进程还没创建
def post_fork(server, worker):
# 这是在新的工作进程中执行的
print(f"4. [Worker {worker.pid}] post_fork: Worker 已 fork,PID: {worker.pid}")
# 在这里为每个 worker 创建数据库连接是最佳实践
# import my_db_lib
# worker.db_conn = my_db_lib.connect()
print(f"[Worker {worker.pid}] 初始化数据库连接...")
def pre_request(worker, req):
worker.log.debug("%s: %s", worker.pid, req.path)
print(f"[Worker {worker.pid}] pre_request: 收到请求 {req.path}")
def worker_int(worker):
# 在这个钩子中,你可以进行优雅的关闭操作
print(f"[Worker {worker.pid}] worker_int: 收到 SIGINT/SIGQUIT 信号,准备退出。")
# if worker.db_conn:
# worker.db_conn.close()
# print(f"[Worker {worker.pid}] 数据库连接已关闭。")
def child_exit(server, worker):
print(f"[Master] child_exit: Worker {worker.pid} 已退出。")
def on_exit(server):
print("[Master] on_exit: Gunicorn 正在关闭...")
使用以下命令来启动 Gunicorn,它会自动加载同目录下的 gunicorn.conf.py
文件:
bash# 确保你在 my_project 目录下
gunicorn my_app:app
如果你想指定配置文件路径:
bashgunicorn --config gunicorn.conf.py my_app:app
启动后,你会在控制台看到类似下面的输出,清晰地展示了钩子的执行顺序:
Gunicorn config file loaded. [2023-10-27 11:30:00] [12345] [INFO] Starting gunicorn 21.2.0 [2023-10-27 11:30:00] [12345] [INFO] Listening at: http://0.0.0.0:8000 (12345) [2023-10-27 11:30:00] [12345] [INFO] Using worker: sync 1. [Master] on_starting: 服务器正在启动... [2023-10-27 11:30:00] [12350] [INFO] Booting worker with pid: 12350 [2023-10-27 11:30:00] [12351] [INFO] Booting worker with pid: 12351 ... (其他 workers) ... 3. [Master] pre_fork: 准备 fork worker N/A... 4. [Worker 12350] post_fork: Worker 已 fork,PID: 12350 [Worker 12350] 初始化数据库连接... 3. [Master] pre_fork: 准备 fork worker N/A... 4. [Worker 12351] post_fork: Worker 已 fork,PID: 12351 [Worker 12351] 初始化数据库连接... ... (其他 workers 的 post_fork) ... 2. [Master] when_ready: 服务器已就绪!
为了让 Gunicorn 更高效地运行,以及便于维护,可以采用以下优化和管理措施。
Gunicorn 支持多种 worker 类,不同的应用场景选择不同的 worker 类可提升性能。
示例:使用 Gevent Worker
bashgunicorn -k gevent -w 4 myproject.wsgi:application
在开发环境中,可以启用自动重新加载功能,使代码更新后自动重启 Gunicorn。
bashgunicorn --reload myproject.wsgi:application
注意: --reload
仅适用于开发环境,不推荐在生产环境中使用。
Supervisor 配置示例:
安装 Supervisor:
bashsudo apt install supervisor
创建 Supervisor 配置文件 /etc/supervisor/conf.d/gunicorn.conf
:
ini[program:gunicorn]
command=/path/to/your/venv/bin/gunicorn --workers 4 --bind 0.0.0.0:8000 myproject.wsgi:application
directory=/path/to/your/myproject
user=your_user
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/gunicorn/gunicorn.log
启动 Supervisor:
bashsudo supervisorctl reread sudo supervisorctl update sudo supervisorctl status gunicorn
logrotate
等工具管理日志文件,防止日志文件过大。logrotate 配置示例 /etc/logrotate.d/gunicorn
:
ini/var/log/gunicorn/*.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 your_user www-data sharedscripts postrotate systemctl reload gunicorn > /dev/null 2>&1 || true endscript }
问题: 启动 Gunicorn 时提示端口被占用。
解决:
检查端口占用情况:
bashsudo lsof -i :8000
杀掉占用进程:
bashsudo kill -9 <PID>
或更改 Gunicorn 绑定的端口。
问题: 通过浏览器访问 Nginx 代理的地址,但无法连接到应用。
解决:
检查 Gunicorn 是否在运行:
bashsudo systemctl status gunicorn
检查 Nginx 配置是否正确,尤其是 proxy_pass
地址和端口。
查看防火墙设置,确保所用端口开放。
问题: Gunicorn 工作进程频繁崩溃或重启。
解决:
timeout
设置,避免请求处理过长时间导致超时。本文作者:Silon汐冷
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!