fastapi + uvicorn 是高并发 python 微服务的事实标准:fastapi 基于 async/starlette,uvicorn 以 uvloop 和 httptools 实现高性能 http 解析,单进程实测中等模型推理场景超 3000 rps,远超 flask 同配置表现。

FastAPI + Uvicorn 是高并发 Python 微服务的默认起点
不是“可以选”,而是当前事实标准:FastAPI 默认基于 async 函数 + Starlette 异步内核,Uvicorn 作为 ASGI 服务器用 uvloop 和 httptools 实现高性能 HTTP 解析。单进程轻松支撑 3000+ RPS(实测中等模型推理场景),远超 Flask 同配置表现。
关键点在于——它不强制你写异步代码,但一旦你把耗时操作(如模型加载、IO 等)移出主线程,吞吐量就立刻拉开差距。比如:
-
joblib.load()放在startup事件里,而非每次请求都调用 - 用
async def predict()包裹轻量逻辑,但真正耗 CPU 的推理仍走线程池(run_in_executor) - 禁用
debug=True上生产,否则 Uvicorn 会退化为同步模式
模型加载必须脱离请求生命周期
常见错误是把 model = joblib.load("model.pkl") 写在路由函数里,导致每次请求都反序列化一次。这不仅慢,还会因 GIL 锁死并发线程。
正确做法是利用 FastAPI 的生命周期钩子:
- 在
@app.on_event("startup")中一次性加载模型到全局变量或依赖容器 - 若模型太大(>1GB),考虑用
mmap或 ONNX Runtime 的 lazy loading 模式 - 多模型服务时,用字典缓存不同版本:
models["v2.1"] = load_model("v2.1.onnx")
注意:PyTorch 模型需调用 .eval() 和 .to("cpu")(除非你部署了 GPU 节点并做了显存隔离)
不要在 FastAPI 里直接跑长任务,用 Celery + Redis/Kafka 分流
FastAPI 不适合处理 >5s 的阻塞任务(如批量预测、特征工程)。强行挂起会导致连接池耗尽、健康检查失败、K8s 误判为 CrashLoopBackOff。
典型解法是分层:
- FastAPI 接收请求 → 校验参数 → 投递
celery.send_task("predict_batch", args=[...]) - Celery Worker 在后台执行,结果写入 Redis 或数据库
- 客户端轮询
/task/{id}/status或通过 WebSocket 推送完成事件
别用 threading 或 asyncio.create_task() 替代 Celery:前者无法跨进程扩展,后者在 Uvicorn 多 worker 下不共享状态,任务会丢失。
Docker 镜像要瘦身,但不能牺牲可复现性
一个典型的 Dockerfile 容易踩两个坑:要么太重(装了 gcc、jupyter 等开发工具),要么太脆(用 pip install -r requirements.txt 直接拉最新版,某天 numpy 升级导致模型输出偏移)。
稳妥做法:
- 基础镜像用
python:3.11-slim-bookworm,非alpine(某些科学计算库编译不兼容) -
requirements.txt必须带 hash(用pip-compile --generate-hashes生成) - 模型文件不 COPY 进镜像,改用
initContainer或 S3 挂载(K8s 场景),避免镜像体积膨胀且难以灰度
最常被忽略的一点:Uvicorn 的 --workers 数不能简单设为 $(nproc)。Python 多进程受 GIL 限制,对纯 CPU 型模型推理,2–4 个 worker 往往比 8 个更稳——因为上下文切换开销反而压垮了吞吐。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











