bigywater.tech
Flask · 142

Greenlet 与 Flask Context

gevent 为什么能让 Flask 同时处理上百个等待中的请求?request 与 g 会不会在 greenlet 之间串?纯计算的请求会怎样?threading.local 要不要 patch?这篇用真实的 gunicorn gevent worker 把这些量出来。

1.05 秒
gevent 单进程处理 100 个各睡 1 秒的请求的总耗时
0 个
100 个并发请求里 g 读到别人的值的个数
C, C, C
未 patch 的 threading.local 在三个 greenlet 里读回的值

gevent 并发等待请求

实测 · 本机执行输出
  100 个并发请求总耗时 1.05 秒 | 进程数 1 | threading.get_ident() 有 100 个不同的值(每个请求一个 greenlet)| g 读回别人的值的有 0 个

上一篇:WSGI 到 ASGI 的适配;下一篇:Thread Local 与请求隔离。

与线程的对比

实测 · 本机执行输出
  gthread 1×8 线程             100 个请求总耗时 13.13 秒
  sync 1 个 worker(只测 8 个)    8 个请求总耗时 8.05 秒

协作式的代价

实测 · 本机执行输出
  一个约 0.8 秒的纯计算请求进行中,一个 /fast 请求用了 369 ms(gevent 下没有抢占,要等计算让出)
  gthread 同样的场景:/fast 用了 40 ms(线程由 GIL 轮流调度,有切换)

threading.local 与 contextvars

实测 · 本机执行输出
  未 patch: {'A': 'C', 'B': 'C', 'C': 'C'}
  patch_all 之后(新建的 threading.local): {'A': 'A', 'B': 'B', 'C': 'C'}
  contextvars(不需要 patch): {'A': 'A', 'B': 'B', 'C': 'C'}

分步演示

Greenlet 与 Flask Context
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。gevent 26.9.0、greenlet、gunicorn gevent worker
    • 没有验证:eventlet、Flask-SocketIO 在 gevent 下的行为、数据库驱动的 gevent 兼容(psycogreen 等)、monkey patch 的顺序问题与 SSL 递归错误、greenlet 栈内存开销
    京ICP备2026067502号-1京公网安备11010502063487号