bigywater.tech
Flask · 144

Gunicorn Worker 类型

gunicorn 的 sync、gthread、gevent 到底怎么选?等待型与轻量请求的差别有多大?为什么 sync worker 前面一定要有反向代理?这篇在同样的槽位数下把三种类型的并发、内存与慢客户端的表现对照出来。

8 / 19 / 83
/wait 等待型负载的请求/秒:sync×4、gthread 2×8、gevent×2
10 秒超时
一个卡着的慢连接让 sync 单 worker 无法处理其它请求
keep-alive
sync worker 不支持,gthread 与 gevent 支持

三种类型的对比

实测 · 本机执行输出
  sync × 4                         空闲内存   165 MB(5 个进程) | /hello 32 并发   3295 请求/秒 | /wait 0.5 秒 × 128 个、64 并发:    8 请求/秒(理想 128)| keep-alive 请求数 0
  gthread × 2 个 worker × 8 线程      空闲内存    98 MB(3 个进程) | /hello 32 并发   3147 请求/秒 | /wait 0.5 秒 × 128 个、64 并发:   19 请求/秒(理想 128)| keep-alive 请求数 4000
  gevent × 2 个 worker              空闲内存   116 MB(3 个进程) | /hello 32 并发   3032 请求/秒 | /wait 0.5 秒 × 128 个、64 并发:   83 请求/秒(理想 128)| keep-alive 请求数 4000

上一篇:Thread Local 与请求隔离;下一篇:多进程下的全局变量。

压测用系统自带的 ApacheBench,与被测服务同机运行,每个数字只测一次,只看相对关系。进程数包含 master 进程。

慢客户端

实测 · 本机执行输出
  sync × 1                         一个连接卡着没发完请求头时,另一个正常请求 → TimeoutError(等了 10003 ms)
  gthread × 1 个 worker × 8 线程      一个连接卡着没发完请求头时,另一个正常请求 → 200,用时 9 ms
  gevent × 1 个 worker              一个连接卡着没发完请求头时,另一个正常请求 → 200,用时 8 ms

分步演示

Gunicorn Worker 类型
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。gunicorn sync、gthread、gevent worker、ApacheBench
    • 没有验证:uvicorn-worker 与 eventlet worker、多次重复测量的方差、worker 的 max_requests 与 preload 的效果、--timeout 对慢客户端的处理、真实 nginx 在前面时 sync worker 的表现
    京ICP备2026067502号-1京公网安备11010502063487号