不能直接用uvicorn main:app跑生产环境,因其单进程模型导致服务脆弱、多核闲置、--reload引发频繁fork;--workers仅在--master下生效但回收不稳;须搭配Gunicorn+UvicornWorker并正确配置timeout、keep-alive、preload及workers数,配合Nginx实现故障隔离、TLS终止与连接复用。

为什么不能直接用 uvicorn main:app 跑生产环境
因为它是单进程模型,一个崩溃整个服务就 502;CPU 多核全闲置;--reload 会监听文件并反复 fork,生产环境根本禁不起这种折腾。更隐蔽的问题是:Uvicorn 的 --workers 参数只在 --master 模式下才真正生效,而这个模式本身不是为高可用设计的——worker 异常退出后回收不稳,日志错乱,连接状态丢失都可能发生。
gunicorn -k uvicorn.workers.UvicornWorker 必须配对的关键参数
光指定 worker 类型远远不够,这几个参数设错,等于白搭:
-
--timeout 120:必须设,但别低于 60。FastAPI 里有数据库慢查询、文件上传、第三方 API 调用等场景,设太短(比如 30)会导致正常请求被粗暴 kill,引发雪崩 -
--keep-alive 5:建议 5–30,但必须 ≤ Nginx 的proxy_read_timeout,否则连接在 Gunicorn 层还活着,Nginx 却已断开,客户端收到 RST -
--preload:一定要开。它让主进程先加载main:app,再 fork worker,保证所有 worker 共享同一份应用对象(比如全局缓存、数据库连接池初始化)。不开的话,每个 worker 各自 import 一次,可能触发重复初始化或状态不一致 -
--workers数值别硬套 CPU×2:I/O 密集型服务(FastAPI 大多如此)设为2 × CPU 核数 + 1更稳妥。8 核机器设 17 个 worker 反而因上下文切换拖慢响应
Nginx 不是可有可无的“转发层”
它实际承担三项不可替代职责:
- 故障隔离:
proxy_next_upstream error timeout http_502 http_503这行必须写进 upstream 配置,否则某个 Gunicorn worker 挂了,Nginx 还继续往它身上转发请求 - TLS 终止:把 HTTPS 解密卸载到 Nginx,避免每个 Uvicorn worker 都做加解密,节省 CPU
- 连接复用管理:Uvicorn worker 的 keep-alive 是面向客户端的,而 Nginx 到 worker 的 upstream 连接是另一层,需要单独控制
keepalive指令(比如keepalive 32)
哪怕只部署一台机器,也该用 upstream 定义多个端口(如 :8000、:8001),否则 Nginx 失去 failover 能力。
日志和子进程输出容易丢的两个坑
很多人发现 Gunicorn 启动后,代码里的 print() 或 logging.info() 在容器里看不到,原因就在这两处:
- 没加
--capture-output:Gunicorn 默认不捕获 worker 子进程 stdout/stderr,print直接被丢弃 - 没配
--access-logfile -和--error-logfile -:这两个-表示输出到 stdout,才能被 Docker 或 systemd 正确采集;漏掉任意一个,访问日志或错误堆栈就进不了日志系统
另外,uvicorn.workers.UvicornWorker 内部默认关闭 access log,所以必须靠 Gunicorn 主进程来统一输出,不能依赖 Uvicorn 自己的日志开关。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











