uvicorn reload 模式下找不到文件:用 file 解决相对路径问题

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;
}

weightleast_conn 在只有一个节点时没有任何效果,max_fails + fail_timeout 仍然生效(健康检测)。