Uvicorn多worker模式仅在存在同步阻塞或CPU密集型逻辑时有效;纯异步I/O服务启用反而降低吞吐。必须用--workers的场景包括:含同步调用导致事件循环卡死、有CPU密集操作、CPU利用率低但QPS上不去、GPU推理服务显存闲置。

Uvicorn 多 worker 模式不是“加数字就变快”,它只在应用存在同步阻塞或 CPU 密集型逻辑时才真正起效;纯异步 I/O 服务开多 worker 反而增加调度开销,吞吐可能下降。
什么时候必须用 --workers?
单进程 Uvicorn 的 event loop 能高效处理成百上千并发请求,但以下任一情况出现,就必须考虑多 worker:
- 代码里有
time.sleep()、requests.get()、pandas.read_csv()等同步调用——哪怕只有一处,整个事件循环就卡死 - 业务含 CPU 密集操作,比如图像预处理、文本向量化、模型推理前的特征工程
- 压测时 CPU 利用率长期低于 60%,但 QPS 上不去,说明单进程无法吃满多核
- 部署 GPU 推理服务(如 M2LOrder),单 worker 只能串行发 batch,显存和算力大量闲置
--workers 数值怎么定?别套公式
所谓 “2×CPU 核数 + 1” 是误导性经验。真实决策应基于三件事:
- 查物理核心数:
nproc输出是 4,--workers 4是安全起点;超过 8 个后吞吐增长通常趋缓 - 看内存容量:每个 worker 是独立 Python 进程,加载一次模型就占一份显存/CPU 内存;RTX 3060(12GB)最多扛住 4 个 worker,再多会 OOM
- 看请求模式:如果请求极不均匀(比如每分钟来 100 个大 batch,其余时间空闲),worker 数宁少勿多,避免空转开销
多 worker 下的 lifespan 和全局变量有多危险?
startup/shutdown 会在每个 worker 进程里单独执行一次,不是“整个服务只跑一次”。所有模块顶层初始化的行为都会被复制 N 份:
立即学习“Python免费学习笔记(深入)”;
- 全局字典
cache = {}在每个 worker 里都是独立副本,互相不可见 - 数据库连接池、Redis 客户端、日志 handler 若在模块顶层初始化,会被复制 N 份,可能触发
psycopg2.OperationalError: too many clients或日志重复 - 启动逻辑(如模型加载、向量索引构建)必须放在
lifespan里,而不是模块顶层 - 共享资源(DB、cache、metrics)应通过依赖注入或 lazy init 控制生命周期,不要依赖
atexit或del
最易被忽略的是:多 worker 模式下,你写的“全局状态”根本不是全局的——它只是每个进程自己的幻觉。


















