应服务启动时一次性加载模型至内存,避免路由中重复反序列化;用 Gunicorn 多 worker 部署替代 app.run();显式调用 request.get_json() 解析 JSON;/health 和 /version 提供健康检查与版本标识。

Flask 启动时模型加载慢,怎么避免每次请求都反序列化?
模型文件(比如 joblib 或 pickle)不能在路由函数里反复 load,否则每来一个请求就磁盘读一次、解包一次,延迟直接拉满,还可能并发冲突。
正确做法是服务启动时一次性加载进内存,作为全局变量或单例存在。注意别放在 if __name__ == '__main__': 里——用 Gunicorn 或 uWSGI 部署时,那个块根本不会执行。
- 把
joblib.load('model.pkl')放在 Flask 实例创建之后、app.run()之前 - 用
@app.before_first_request不可靠(已被弃用,且多进程下不生效) - 如果模型大,考虑加个
logging.info打印加载耗时,方便定位冷启瓶颈 - 路径写绝对路径,别用
os.getcwd()—— Gunicorn 工作目录和你本地开发路径通常不一致
POST JSON 数据预测时报 400 Bad Request,常见原因有哪些?
不是模型错了,是 Flask 默认不自动解析 JSON body,或者前端发的格式不对。
- 确保前端请求头带
Content-Type: application/json - 后端必须显式调用
request.get_json(),而不是直接读request.data或request.form - 如果 JSON 里字段名和模型训练时的列名不一致(比如多了
id字段),sklearn会直接抛ValueError: X has 5 features, but LinearRegression is expecting 4 features - 空值(
null)传进来会被转成 PythonNone,而大多数 sklearn 模型不接受None,得提前用np.nan替换或做缺失值处理
部署后 CPU 占用高、响应变慢,是不是 Flask 本身扛不住?
不是 Flask 不行,是默认的开发服务器 app.run() 是单线程、非生产级的。它只能串行处理请求,模型推理一卡,后面全排队。
立即学习“Python免费学习笔记(深入)”;
- 绝不能用
app.run(host='0.0.0.0', port=5000)直接上线 - 用
Gunicorn:启动命令类似gunicorn -w 4 -b 0.0.0.0:5000 app:app,其中-w是 worker 数,建议设为 CPU 核数 × 2 - 如果模型本身是 CPU 密集型(比如树模型、SVM),再多 worker 也容易打满 CPU;这时要考虑加
threadpoolctl限制 sklearn 内部线程数,避免每个 worker 又开满所有核 - 检查有没有在预测逻辑里做日志写文件、同步数据库查询等阻塞操作——这些该异步的得拆出去
如何让 Flask API 支持健康检查和模型版本标识?
运维和监控需要快速知道服务是否存活、当前跑的是哪个模型。硬编码 /health 和 /version 是最简单有效的方案。
-
/health返回{"status": "ok", "timestamp": ...},不碰模型、不查库,纯内存响应 -
/version返回{"model_hash": "sha256:...", "trained_at": "2024-05-10"},hash 建议在训练完保存模型时就计算好并写进元数据文件,别每次启动再算 - 别把模型路径或本地文件名暴露在接口里(比如返回
/models/v2/model.pkl),容易引发路径泄露风险 - 如果用 Docker,可以把
MODEL_VERSION设为环境变量,在 Flask 里读取,避免改代码发版
sklearn 的 predict 方法默认不支持批量输入的 list of dict,得先转成 pd.DataFrame 或 np.ndarray,这个转换逻辑很容易被忽略,一到线上就报 ValueError: Expected 2D array, got 1D array instead。










