uvicorn 加 --reload 参数时,每次代码变更都会重启子进程。子进程在 import 模块时如果代码顶层有相对路径操作,很容易报 FileNotFoundError。
问题复现
# main.py
import cv2
img = cv2.imread("small.png", 0) # 顶层直接读取
uvicorn main:app --reload
启动会报:
FileNotFoundError: [Errno 2] No such file or directory: 'small.png'
原因:uvicorn reload 子进程的 工作目录(cwd) 可能和你预期的不一致,相对路径 "small.png" 找的是 cwd 下的文件而不是脚本旁边的文件。
解决:用 __file__ 构造绝对路径
from pathlib import Path
BASE_DIR = Path(__file__).parent
img = cv2.imread(str(BASE_DIR / "small.png"), 0)
Path(__file__).parent 是脚本自身所在目录,不受 cwd 影响,任何启动方式都一致。
统一管理项目路径
from pathlib import Path
BASE_DIR = Path(__file__).parent
MODELS_DIR = BASE_DIR / "models"
DATA_DIR = BASE_DIR / "data"
CONFIG_FILE = BASE_DIR / "config.yaml"
然后全局使用 BASE_DIR / "xxx" 而不是裸字符串路径。
uvicorn 启动方式
字符串形式(支持 reload):
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=True
)
对象形式(不支持 reload):
uvicorn.run(
app, # FastAPI 对象
host="0.0.0.0",
port=8000
# reload=True 在这里无效,reload 必须用字符串形式
)
reload 模式会 fork 子进程并 import 指定模块,所以 module 名必须是字符串。
GPU 模型服务的 worker 数
如果 FastAPI 服务里加载了 GPU 模型(SAM、YOLO、PyTorch 等),只开 1 个 worker:
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 1
多 worker 会导致每个进程加载一份模型,显存翻倍。CPU 服务才适合 workers = CPU核数 × 2 + 1。
Nginx upstream 与单进程
单进程 FastAPI 的 upstream 可以简化:
upstream backend {
server 127.0.0.1:18001;
}
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
weight、least_conn 在只有一个节点时没有任何效果,max_fails + fail_timeout 仍然生效(健康检测)。
