wrk测试前必须确认:①Flask走WSGI无原生异步,FastAPI基于ASGI+uvicorn才启用事件循环;②数据库层需匹配(psycopg2同步vs asyncpg异步);③wrk参数(-c、-t、-d)、URL、响应体及依赖必须严格一致。

用 wrk 测试前必须确认的 3 个底层差异
Flask 默认走 WSGI,根本不能原生处理异步请求;FastAPI 基于 ASGI,uvicorn 启动后才真正启用事件循环。直接拿 flask run 和 uvicorn main:app 对比,结果毫无意义——前者压根没进异步通道。
常见错误现象:wrk -c 100 -t 4 -d 30 http://localhost:8000/api 测 Flask 时 QPS 突然卡在 1200 左右,不是代码慢,是 Gunicorn 默认只开 4 个同步 worker,每个 worker 一次只能扛一个阻塞请求。
- Flask 要测“伪异步”,得配
gunicorn --worker-class gevent或--worker-class eventlet,但 ORM(如 SQLAlchemy)和 HTTP 客户端(如requests)仍可能同步阻塞 - FastAPI 测真异步,必须用
uvicorn main:app --workers 1 --loop uvloop:禁用多进程,靠单 worker +uvloop榨干事件循环 - 数据库层必须对齐:Flask 用
psycopg2是同步的,FastAPI 用asyncpg才算匹配;混用会导致 FastAPI 的 async route 实际被psycopg2拖回同步模型
wrk 命令参数要严格一致才能横向对比
很多人用不同并发数、不同连接数去跑两个框架,结果出来就喊“FastAPI 快 7 倍”,其实只是参数没控住。真实压测必须固定三要素:-c(总连接数)、-t(线程数)、-d(持续时间),且 URL 路径、响应体大小、后端依赖(DB/HTTP 调用)完全一致。
推荐命令:
立即学习“Python免费学习笔记(深入)”;
wrk -c 1000 -t 8 -d 60 --latency http://localhost:8000/api/query
说明:
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
-
-c 1000表示维持 1000 个 HTTP 连接,这对异步框架很关键——FastAPI 能复用单 worker 处理全部连接,Flask 却需要至少 1000 / worker 并发能力,否则排队 -
-t 8是 wrk 自身线程数,设太高反而因上下文切换拖慢,8 是多数 4 核机器的平衡点 - 务必加
--latency:不光看 QPS,更要看Latency Distribution里 90%/99% 分位值,Flask 在高并发下尾部延迟常飙升到秒级,FastAPI 通常稳定在百毫秒内
测试代码里最容易忽略的 I/O 绑定陷阱
写个 time.sleep(0.1) 看起来是“模拟耗时”,但它在 Flask 和 FastAPI 里语义完全不同:前者真停线程,后者只是让出控制权。真实瓶颈从来不是 CPU,而是数据库查询、外部 API 调用、文件读写——这些才是压测该打的靶子。
正确做法:
- FastAPI 接口必须用真正异步 I/O:比如
await database.fetch_one(...)(databases库)或await asyncpg.fetch(...),而不是db.execute(...)包一层asyncio.to_thread - Flask 若强行用
gevent.spawn包requests.get,要注意requests底层 socket 默认是阻塞模式,需提前打补丁:from gevent import monkey; monkey.patch_socket() - 所有测试接口返回体统一为
{"status": "ok", "ts": 1714654560}这种固定长度 JSON,排除序列化开销干扰
看 QPS 数字之前先盯住内存与错误率
很多团队测完发现 FastAPI QPS 高但内存涨得快,一查是 uvicorn 默认没设 --limit-concurrency,1000 个连接全涌进来,协程对象堆积导致 OOM。Flask 反而稳,因为 Gunicorn 的 worker 进程天然隔离。
关键配置项:
- FastAPI 生产部署必加:
uvicorn main:app --limit-concurrency 1000 --limit-max-requests 10000,防长连接耗尽资源 - Flask 的 Gunicorn 要配
--max-requests 1000 --max-requests-jitter 100,避免 worker 内存泄漏累积 - wrk 输出里重点看
Non-2xx or 3xx responses:FastAPI 异步超时若没配asyncio.wait_for,会直接抛500;Flask 用 gevent 时若 socket 补丁漏了,会出现大量Connection refused
真正的性能分水岭不在峰值 QPS,而在高负载下的稳定性——FastAPI 单 worker 能扛住 1000 并发不崩,Flask 得开 10+ gevent worker,这时进程间内存开销、上下文切换成本、监控粒度就全不一样了。


















