从 0 到 1 掌握 Gunicorn:让 Python Web 应用飞起来的 WSGI 服务器

文章目录


前言:为什么"裸跑" Flask/Django 不够?

本地开发时,一条 flask runpython manage.py runserver 就能让应用愉快地跑起来。但这两句命令背后都挂着 单线程、单进程、自动重载、调试钩子 的"开发服务器"标签,一旦放到生产流量下,就会出现:

  • 并发处理能力弱(单进程/线程)
  • 无安全熔断(慢请求会拖垮整个服务)
  • 静态文件效率低
  • 不支持热升级、优雅重启

于是,我们需要一个 专注生产场景的 WSGI 服务器------Gunicorn(Green Unicorn)。


环境说明

操作系统:Ubuntu 24.04.1 LTS

Python 版本:3.10.19

Flask 版本:3.1.2

Gunicorn 版本:23.0.0

Visual Studio Code 版本:1.106.3

Visual Studio Code 插件:Python、Pylance、Docker
插件可参考:https://blog.csdn.net/achi010/article/details/154604215


1、Gunicorn 是什么?

  • 用 Python 写的轻量级 WSGI HTTP Server
  • 诞生于 2010 年,目前由 Benoit Chesneau 等人维护
  • 口号:"WSGI HTTP Server for UNIX",定位就是 Unix/Linux 上跑 Python Web 的生产入口
  • 支持多种并发模型(sync、gevent、eventlet、tornado、meinheld、uvloop...)
  • 零配置即可跑,也提供 100+ 可调参数,可玩性高

官网:https://gunicorn.org/

部署相关文档(Nginx 相关配置注意事项):https://docs.gunicorn.org/en/latest/deploy.html

GitHub:https://github.com/benoitc/gunicorn

Gunicorn --- Flask Documentation (3.1.x)


2、安装 & 最小可运行示例

(1)安装

bash 复制代码
python -m venv venv
source venv/bin/activate
pip install gunicorn flask

(2)示例应用 app.py

python 复制代码
from flask import Flask, jsonify
import os

app = Flask(__name__)

@app.get("/")
def index():
    return jsonify(
        pid=os.getpid(),
        msg="Hello from Gunicorn!"
    )

(3)启动测试

bash 复制代码
gunicorn app:app -b 0.0.0.0:8000

访问 http://localhost:8000 ,看到返回的进程 PID,说明 Gunicorn 已经在背后监听 8000 端口。


3、核心概念速览

术语 含义
Worker 实际处理请求的进程/线程
Master 管理 Worker 生命周期的守护进程
Pre-fork Master 先 fork() 出 N 个 Worker,再共同 accept() 同一个 Socket
Sync 默认模型,每个 Worker 一次只处理一个请求(适合 CPU 密集)
Async 基于 gevent/eventlet,一个 Worker 可并发上千个绿色线程(适合 I/O 密集)
graceful timeout 给 Worker 的最大"优雅"处理时间,超时则强制 SIGKILL

4、并发模型怎么选?

(1)CPU 密集(图像处理、加密、大数据计算)

推荐: syncgthread,Worker 数 ≈ CPU 核数

bash 复制代码
gunicorn app:app -w 4 -k sync
# 或者
gunicorn app:app -w 4 -k gthread

(2)I/O 密集(代理、API Gateway、爬虫聚合)

推荐: gevent

bash 复制代码
pip install gevent
gunicorn app:app -k gevent -w 2 --worker-connections 1000

(3)混合场景(有点复杂,暂不讨论)

gthread 兜底,或者把 CPU 任务拆到 Celery,Web 层保持轻量


5、生产级配置清单(可直接抄)

配置参数 - 官方说明文档:https://docs.gunicorn.org/en/stable/settings.html#server-mechanics

conf 复制代码
# gunicorn.conf.py
# 绑定地址和端口
bind = "0.0.0.0:8000"

# 工作进程数量,通常设置为CPU核心数的2倍加1
workers = 4

# 工作进程类型,使用gevent异步模式或同步模式"sync"
worker_class = "gevent"

# 每个工作进程的最大并发连接数
worker_connections = 1000

# 每个工作进程处理多少个请求后重启,防止内存泄漏
max_requests = 10000

# max_requests的抖动值,避免所有工作进程同时重启
max_requests_jitter = 500

# 优雅关闭超时时间(秒)
graceful_timeout = 30

# HTTP keep-alive持续时间(秒)
keepalive = 2

# 是否预加载应用,可以节省内存并提高性能
preload_app = True

# 访问日志格式定义
access_logformat = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'

# 访问日志输出位置,"-"表示标准输出
accesslog = "-"

# 错误日志输出位置,"-"表示标准输出
errorlog = "-"

# 日志级别:debug, info, warning, error, critical
loglevel = "info"

启动:

bash 复制代码
gunicorn app:app -c gunicorn.conf.py

6、优雅重启 & 零宕机发布

(1)收到 HUP 信号后,Master 会重新载入代码,新 Worker 逐步替换旧 Worker,旧请求不受影响:

bash 复制代码
kill -HUP <master_pid>

(2)如果在容器中,可把 PID 1 换成 gunicorndocker stop 会自动发 SIGTERM,Gunicorn 会等待 graceful_timeout,再退出。

(3)蓝绿 / 滚动发布:结合 K8s RollingUpdate + readinessProbe,实现 零中断。


7、容器化最佳实践

(1)Dockerfile(多阶段、非 root、健康检查)

dockerfile 复制代码
# 使用 Python 3.10.19 slim 镜像作为多阶段构建的第一阶段,命名为 builder
# 这个阶段专门用于安装 Python 依赖包
FROM python:3.10.19-slim AS builder
# 设置 pip 的国内镜像源为默认
ENV PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
# 设置工作目录为 /app
WORKDIR /app
# 将项目中的 requirements.txt 文件复制到容器的 /app 目录中
COPY requirements.txt .
# 在 builder 阶段安装项目所需的 Python 依赖包到用户目录
RUN pip install --user -r requirements.txt

# 开始第二阶段构建,使用相同版本的 Python 基础镜像
FROM python:3.10.19-slim
# 设置环境变量 PATH,使用户安装的包可以在命令行中直接使用
ENV PATH=/home/app/.local/bin:$PATH
# 创建一个 UID 为 1000 的 app 用户并创建其主目录
# 这样避免以后以 root 用户身份运行应用,提高安全性
RUN useradd -m -u 1000 app
# 设置工作目录为 /app
WORKDIR /app
# 从第一阶段(builder)复制已安装的 Python 包到 app 用户的本地目录
COPY --from=builder /root/.local /home/app/.local
# 将项目中的所有文件复制到容器的 /app 目录中
COPY . .
# 切换到 app 用户执行后续指令,增强容器安全性
USER app
# 声明容器运行时监听的端口为 8000
EXPOSE 8000
# 配置健康检查,通过 Python 脚本发送 HTTP 请求检测应用是否正常运行
HEALTHCHECK CMD python -c "import requests; requests.get('http://localhost:8000/healthz', timeout=3)"
# 容器启动时执行的命令:使用 gunicorn WSGI 服务器启动 Flask 应用
# app:app 表示导入 app 模块中的 app 对象
# -c gunicorn.conf.py 表示使用 gunicorn.conf.py 配置文件
CMD ["gunicorn", "app:app", "-c", "gunicorn.conf.py"]

(2)构建镜像

bash 复制代码
docker build -t app:0.1 .

(3)运行测试

bash 复制代码
docker run --name app -p 8000:8000 -d app:0.1

浏览器访问:http://localhost:8000 看到返回的进程 PID,说明容器正常启动


8、常见坑 8 则

  1. 忘记设置 forwarded-allow-ips

    用 Nginx 做反向代理时,需把真实 IP 传过去:

    forwarded-allow-ips = "*" 或具体 CIDR

  2. 静态文件 404

    Gunicorn 只负责 动态 WSGI,静态资源应交给 Nginx/Caddy/S3

  3. Worker 内存暴涨

    设置 max_requests + max_requests_jitter,让 Worker 定期"重生"

  4. HTTPS 重定向死循环

    在代理层加 X-Forwarded-Proto: https,并在 Flask 加 ProxyFix

  5. Docker 里 PID 1 收不到信号

    exec gunicorn ... 启动,或加 tini 作为 init

  6. Gevent 与 C 扩展冲突

    部分库(如 psycopg2)需改用 纯 Python 或协程友好 版本(psycopg2-binarypsycopg[binary]

  7. 日志时间戳不对

    容器内默认 UTC,可在 gunicorn.conf.py 里:

    access_log_format = '%(t)s %(h)s "%(r)s" %(s)s %(b)s'

  8. 高并发下端口耗尽

    打开 net.ipv4.tcp_tw_reuse = 1,或让客户端走连接池


小结 & 延伸阅读

  • Gunicorn 不是银弹,而是 WSGI 的最后一公里。合理选择并发模型 + 参数调优,才能发挥多核威力。
  • 源码仅 7k 行,注释友好,推荐阅读 gunicorn/workers/ 下各种模型的实现差异。
  • 进一步学习 ASGI 相关知识

相关推荐
坐吃山猪2 小时前
Python之PDF小工具
开发语言·python·pdf
小鸡吃米…2 小时前
Python - 构造函数
开发语言·python
Lenyiin2 小时前
第 97 场周赛:公平的糖果交换、查找和替换模式、根据前序和后序遍历构造二叉树、子序列宽度之和
java·c++·python·leetcode·周赛·lenyiin
znhy_232 小时前
day42打卡
python
SCBAiotAigc2 小时前
在Ubuntu上使用docker compose安装普通(不支持GPU)的Ollama服务
人工智能·python·ubuntu·ollama
小智RE0-走在路上3 小时前
Python学习笔记(10) -- 异常,模块,包
笔记·python·学习
Autumn72993 小时前
【python】 日志打印、垃圾回收
开发语言·python
天呐草莓3 小时前
微信小程序应用开发
python·微信小程序·小程序
Blossom.1183 小时前
知识图谱与大模型融合实战:基于GNN+RAG的企业级智能问答系统
人工智能·python·深度学习·神经网络·微服务·重构·知识图谱