如何在gunicorn部署的FastAPI中高效加载spaCy模型以节省内存
多worker共享spaCy模型解决方案
核心原理
Gunicorn默认使用prefork工作模式,主进程fork出子worker时,只读内存页会在多个子进程之间共享,只有当子进程修改对应内存页时才会触发写时复制单独拷贝一份。只要把spaCy模型的加载逻辑放在主进程fork操作之前执行,就能让多个worker共享同一份模型内存,避免重复加载。
方案1:开启Gunicorn preload模式(改造成本最低)
这是最推荐的方案,不需要大量修改业务代码:
- 把spaCy模型的加载逻辑放在FastAPI入口文件的全局作用域,不要在路由函数、生命周期钩子等子进程启动后才执行的逻辑里加载模型,示例代码如下:
# 项目入口main.py示例 import spacy from fastapi import FastAPI # 全局位置预加载模型,preload模式下由主进程执行 nlp = spacy.load("spacy_en_core_web_lg") app = FastAPI() # 业务路由直接使用全局nlp对象即可 @app.post("/nlp/extract") def extract_entity(text: str): doc = nlp(text) return [{"text": ent.text, "label": ent.label_} for ent in doc.ents]
- 修改Gunicorn启动配置,开启preload功能:
要么在Gunicorn配置文件中添加preload_app = True
要么在启动命令中添加参数:gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --preload - 注意:不要在业务代码中修改全局
nlp对象的任何属性,否则会触发写时复制,还是会在子进程单独生成模型副本占用内存。
方案2:独立部署模型服务
如果你的业务逻辑需要修改模型实例,或者无法使用prefork模式,可以选择将模型推理能力独立部署:
- 启动一个独立的专属进程加载spaCy模型,对外提供HTTP/RPC调用接口
- 所有FastAPI worker不再本地加载模型,都通过调用独立模型服务的接口完成推理
- 优势是模型和Web服务完全解耦,可以单独调整资源配置,缺点是会增加少量接口调用开销
方案3:改用单进程多线程部署
如果请求量不高,可以将Gunicorn的worker数改成1,开启多线程处理请求:
- 启动命令改为
gunicorn main:app --workers 1 --threads 8 --worker-class uvicorn.workers.UvicornWorker - 只有1个worker进程只会加载1份模型,内存占用直接降低75%,线程间可以共享同一份模型实例
内容的提问来源于stack exchange,提问作者swartchris8
相关产品推荐
相关产品推荐

