3. Model Serving 架构设计:整体架构分层与关键组件选型
在风控评分通道中,Model Serving 架构需要同时满足高并发、低延迟、高可用和可观测四大核心要求。本章从整体架构分层出发,逐层拆解设计要点,并对比主流推理框架的选型策略。
3.1 整体架构分层
我们将 Model Serving 系统划分为三个核心逻辑层:接入层、推理层和存储层。每一层职责清晰,层间通过轻量级协议(HTTP/gRPC)解耦,便于独立扩缩容与灰度发布。
设计原则:接入层负责协议转换与流量治理,推理层专注模型计算与资源调度,存储层提供特征与结果持久化。层间不共享内部状态,仅通过 API 契约通信。
3.2 接入层设计要点
接入层是流量的第一道关卡,在风控场景中需要处理每秒数千到数万次评分请求。接入层的主要职责包括:
- 协议转换:客户端通常使用 HTTP/JSON,推理层内部推荐使用 gRPC/Protobuf 以获得更好的序列化性能和强类型约束。
- 限流与熔断:基于令牌桶或滑动窗口算法,防止突发流量冲垮推理节点。熔断策略采用半开状态自动恢复。
- 灰度路由:根据请求头、用户 ID 或模型版本标签,将流量按比例分发到不同推理集群,支持 A/B 测试和金丝雀发布。
- 请求校验与鉴权:校验必填字段、签名、API Key,拒绝无效请求,降低推理层无效计算开销。
| 接入层组件 | 协议支持 | 限流能力 | 灰度路由 | 社区活跃度 |
|---|---|---|---|---|
| Nginx + Lua | HTTP/1.1, HTTP/2 | 高(lua-resty-limit-traffic) | 需自研 | 极高 |
| Kong | HTTP, gRPC | 高(插件生态) | 内置 | 高 |
| Envoy | HTTP/1.1, HTTP/2, gRPC | 高(本地限流+全局限流) | 内置(权重路由) | 极高 |
| Spring Cloud Gateway | HTTP, WebSocket | 中(基于 Redis 的 RequestRateLimiter) | 需自研 | 高 |
选型建议:如果团队已有 Java 技术栈,优先选择 Spring Cloud Gateway + Nginx 组合;如果追求云原生和统一控制面,Envoy 是更优选择。风控场景建议在接入层开启请求级超时控制(通常设为 200ms),避免慢请求阻塞连接池。
3.3 推理层核心设计
推理层是 Model Serving 的心脏,直接承载模型计算。设计时需要重点考虑模型加载策略、批量推理和资源隔离三个维度。
3.3.1 模型加载与热更新
- 冷启动加载:服务启动时从对象存储(S3/MinIO)拉取模型文件到本地缓存,加载到 GPU/CPU 内存。
- 热更新:通过监听模型仓库的版本变更事件(etcd watch / 文件系统 inotify),在不重启服务的情况下切换模型版本。
- 版本管理:每个模型保留最近 N 个版本,支持按版本号或标签(stable/canary)路由请求。
3.3.2 批量推理策略
风控评分模型通常为轻量级树模型或浅层神经网络,单次推理延迟在 1-10ms 量级。批量推理可以显著提升吞吐量:
- 动态批处理:推理框架自动收集请求,在最大延迟容忍度内组成 batch,减少内核启动开销。
- 队列管理:为每个模型维护独立请求队列,设置最大 batch size 和最大等待时间(如 32 条 / 5ms)。
- 优先级调度:高优先级请求(如实时交易评分)可跳过队列直接推理,或插入队列头部。
3.4 关键组件选型:Flask vs FastAPI vs Triton
在推理框架选型上,需要根据模型类型、部署规模和性能要求做出权衡。下表从多个维度对比三个主流选项:
| 维度 | Flask | FastAPI | Triton Inference Server |
|---|---|---|---|
| 定位 | 轻量级 Web 框架 | 高性能异步 Web 框架 | 专用推理服务器 |
| 协议 | HTTP/1.1 | HTTP/1.1, HTTP/2, WebSocket | HTTP, gRPC, 原生 C API |
| 异步支持 | 需额外配置(Gunicorn + gevent) | 原生 async/await | 内置异步调度器 |
| 批量推理 | 需自研 | 需自研 | 内置动态批处理 |
| GPU 管理 | 无 | 无 | 内置 GPU 调度、MPS、并发模型 |
| 模型格式 | 任意(需手动加载) | 任意(需手动加载) | TensorRT, ONNX, PyTorch, XGBoost 等 |
| 生产就绪度 | 中(需大量自建组件) | 高(自动文档、校验、监控) | 极高(企业级部署、健康检查、Prometheus 指标) |
| 适用场景 | 原型验证、低并发内部工具 | 中等并发、需要快速迭代的评分服务 | 高并发、多模型、GPU 密集的风控核心链路 |
避坑提示:不要在生产风控链路中直接使用 Flask 的默认开发服务器(Werkzeug 单进程)。即使使用 Gunicorn + gevent worker,Flask 在处理长连接和流式请求时仍存在性能瓶颈。如果团队人力有限,优先选择 FastAPI + Uvicorn 作为推理层入口,再通过 Triton 作为后端推理引擎,形成“FastAPI 编排 + Triton 计算”的混合架构。
3.5 混合架构推荐:FastAPI + Triton
在风控评分通道的实际落地中,我们推荐采用FastAPI 作为推理编排层,Triton 作为模型计算引擎的混合架构。FastAPI 负责请求解析、特征预处理、结果后处理、调用链追踪;Triton 负责模型加载、GPU 资源调度和批量推理。两者通过 gRPC 通信,兼顾灵活性与性能。
# FastAPI 编排层示例:调用 Triton 进行推理
import tritonclient.grpc as grpcclient
from fastapi import FastAPI, Request
from pydantic import BaseModel
app = FastAPI()
triton_client = grpcclient.InferenceServerClient(url="triton:8001", verbose=False)
class ScoreRequest(BaseModel):
user_id: str
features: list[float]
@app.post("/v1/score")
async def score(request: ScoreRequest):
# 1. 特征预处理(归一化、缺失值填充)
processed_features = preprocess(request.features)
# 2. 构造 Triton 请求
inputs = [grpcclient.InferInput("input", [1, len(processed_features)], "FP32")]
inputs[0].set_data_from_numpy(np.array([processed_features], dtype=np.float32))
outputs = [grpcclient.InferRequestedOutput("output")]
# 3. 调用 Triton 推理
result = triton_client.infer(model_name="xgb_score", inputs=inputs, outputs=outputs)
score = result.as_numpy("output")[0][0]
# 4. 结果后处理(分数映射、阈值判断)
final_decision = postprocess(score)
return {"user_id": request.user_id, "score": float(score), "decision": final_decision}
架构收益:FastAPI 层可以灵活集成自定义预处理逻辑、第三方特征服务调用、缓存策略;Triton 层专注模型计算,支持多模型并发、GPU 共享和自动扩缩容。两者解耦后,模型更新只需重启 Triton 实例,编排层无需变更。
3.6 存储层设计要点
存储层在 Model Serving 中承担特征缓存、评分结果持久化和模型元数据管理三类职责:
- 特征缓存(Redis):缓存实时特征(如设备指纹、IP 画像),减少重复计算。设置 TTL 过期策略,避免冷数据堆积。
- 评分结果持久化(MySQL / ClickHouse):记录每次评分的请求 ID、用户 ID、模型版本、输入特征摘要、输出分数、决策结果、耗时。用于离线分析、模型监控和审计。
- 模型元数据(etcd / ZooKeeper):存储模型版本、路由规则、健康状态。推理层通过 watch 机制感知元数据变更,实现热更新。
| 存储类型 | 推荐组件 | 关键配置 | 注意事项 |
|---|---|---|---|
| 特征缓存 | Redis Cluster | maxmemory-policy allkeys-lru;超时 300s | 避免大 Value(>10KB),防止阻塞 |
| 评分结果 | ClickHouse | MergeTree 引擎;按天分区;TTL 30 天 | 写入 QPS 高时启用批量插入(每 1000 条 flush) |
| 模型元数据 | etcd | 租约 10s;watch 超时 30s | 避免频繁写入,元数据变更频率通常低于 1 次/分钟 |
关键指标:存储层写入延迟应控制在 5ms 以内(P99),否则会影响推理链路的整体耗时。建议将存储写入操作异步化:推理请求返回后,通过消息队列(Kafka)将评分结果异步写入 ClickHouse,避免阻塞主链路。