只用uvicorn.run()启动FastAPI会导致QPS瓶颈,因其单进程、无监控、无优雅重启;必须用Gunicorn管理多进程并指定uvicorn.workers.UvicornWorker,才能释放异步性能——该worker为唯一原生支持FastAPI ASGI生命周期的类型,其他类型会降级或报错。

直接上结论:只用 uvicorn.run() 启动 FastAPI,QPS 上不去不是框架问题,而是单进程 + 无连接管理导致的硬瓶颈;必须用 Gunicorn 管理多进程,并搭配 uvicorn.workers.UvicornWorker 才能真正释放异步性能。
为什么不能只用 Uvicorn 启动?
开发时常见的 uvicorn.run("main:app", host="0.0.0.0", port=8000) 是单 worker、无进程监控、无优雅重启的裸跑模式。在 4 核机器上,它最多只跑满 1 个 CPU 核,其余资源闲置。压测时容易出现:
- 连接排队超时(
ConnectionResetError或503 Service Unavailable) - 长请求阻塞后续请求(尤其含数据库 I/O 时)
- 进程崩溃后服务中断,无自动拉起
这些都不是 FastAPI 的锅,是部署方式没跟上。
必须用 UvicornWorker,别选 sync 或 gevent
uvicorn.workers.UvicornWorker 是唯一能原生承载 FastAPI 异步逻辑的 Gunicorn worker 类型。其他类型会降级或出错:
立即学习“Python免费学习笔记(深入)”;
-
sync:强制把async def当同步函数调,await直接报RuntimeWarning: coroutine 'xxx' was never awaited -
gevent:依赖 monkey patch,与 FastAPI 的 event loop 冲突,大概率触发RuntimeError: There is no current event loop in thread -
uvicorn.workers.UvicornWorker:每个 worker 启一个独立asyncioevent loop,完美匹配 FastAPI 的 ASGI 生命周期
启动命令必须带 -k uvicorn.workers.UvicornWorker,少一个字母都不行。
inference.sh 的 Python SDK:运行 AI 应用、构建智能体,并集成 150 多个模型。包名:inferencesh (pip install inferencesh)。支持同步/异步……
workers 数量不是越多越好
开 16 个 UvicornWorker 在 4 核机器上反而 QPS 下降——每个 worker 都要维护自己的 event loop 和连接池,上下文切换和内存开销剧增。实测最优值接近:
- CPU 密集型接口:
workers = CPU核心数 - I/O 密集型接口(如查 DB、调第三方 API):
workers = (CPU核心数 × 2) + 1,比如 4 核配 9 个 worker - 务必配合
--limit-concurrency 1000防止单 worker 积压过多协程拖垮事件循环
别信“核数×4”这种泛泛而谈的建议,压测时看 gunicorn errorlog 里有没有 Worker failed to boot 或 max_requests limit reached 才是真实信号。
数据库连接池必须对齐 workers
每个 UvicornWorker 进程都需独立连接池,否则会出现 asyncpg.exceptions.TooManyConnectionsError 或连接复用混乱。常见错误写法是全局单例连接对象:
# ❌ 错误:所有 worker 共享一个 connection
database = Database("postgresql://...")
<p>@app.on_event("startup")
async def startup():
await database.connect() # 只在主进程执行,子进程拿不到
正确做法是每个 worker 启动时单独建池:
# ✅ 正确:on_event 绑定到每个 worker 实例
@app.on_event("startup")
async def startup():
await database.connect() # 每个 worker 进程都会执行
<p>@app.on_event("shutdown")
async def shutdown():
await database.disconnect()
连接池大小建议设为 min_size = workers, max_size = workers * 3,留余量给后台任务和突发流量。

















