一句话总结
模型导出为 ONNX 只是第一步--要让它在生产环境稳定服务,还需要一个推理服务器来管理模型加载, 请求调度, 版本切换和健康检查.
本篇对比三种主流方案,选出最适合 Go 后端集成的架构.
前置回顾
第 15 篇把 PyTorch 模型导出为 ONNX 格式,验证了导出文件的正确性和推理加速效果.
现在的问题是:这个 .onnx 文件怎么在线上跑起来,对外提供 API?
为什么不能直接在 Go 里加载 ONNX
最简单的方案:Go 服务启动时用 onnxruntime-go 直接加载模型文件,请求来了就推理.
这在 demo 阶段可行,但生产环境有几个致命问题:
- 模型更新要重启服务 -- 新版本模型上线意味着 Go 进程重启,影响在线流量
- 资源隔离困难 -- 模型推理是 CPU/GPU 密集型,和业务逻辑挤在同一进程,互相争抢资源
- 无法独立扩缩容 -- 推理负载高时,只能整个 Go 服务扩容,浪费业务逻辑部分的资源
- 多模型管理复杂 -- 群聊审核可能同时需要情感模型, 意图模型, 毒性模型,全部塞进一个进程难以维护
解决方案:把模型推理拆成独立服务,Go 业务层通过 gRPC/HTTP 调用.
|
推理服务 = 数据库. 没有人会把 MySQL 嵌入到 Go 进程里(虽然 SQLite 能做到). 数据库独立部署,应用通过网络协议访问. 模型推理也是同理--计算密集, 有状态(模型文件), 需要独立管理的基础设施.
三种主流推理服务器
NVIDIA Triton Inference Server
专为高性能推理设计的服务器,支持多种模型格式:
| 维度 | 说明 |
|---|---|
| 支持格式 | ONNX, TensorRT, PyTorch, TensorFlow, Python 自定义 |
| 通信协议 | gRPC + HTTP/REST |
| 核心能力 | Dynamic batching, 模型并发执行, GPU 实例分组 |
| 模型管理 | 文件系统目录结构,支持热加载和版本管理 |
| 适用场景 | 有 GPU,追求极致吞吐的在线推理 |
Triton 的模型仓库结构:
|
|
TorchServe
PyTorch 官方推理服务,适合纯 PyTorch 生态:
| 维度 | 说明 |
|---|---|
| 支持格式 | TorchScript, eager mode PyTorch |
| 通信协议 | HTTP/REST (gRPC 支持有限) |
| 核心能力 | 自定义 handler, 模型归档(.mar), 管理 API |
| 模型管理 | .mar 打包文件,通过 Management API 注册/删除 |
| 适用场景 | 纯 PyTorch 模型,不需要 ONNX 转换 |
TorchServe 与 Go 集成的局限
TorchServe 的 gRPC 支持不如 Triton 成熟,主要依赖 HTTP. 对于追求低延迟的 Go 后端,HTTP 序列化开销比 gRPC 高不少. 如果你的模型已经导出为 ONNX,TorchServe 不是最佳选择.
FastAPI + ONNX Runtime
自己写一个 Python Web 服务,用 ONNX Runtime 加载模型:
|
| 维度 | 说明 |
|---|---|
| 支持格式 | ONNX (通过 onnxruntime) |
| 通信协议 | HTTP/REST, 可自行加 gRPC |
| 核心能力 | 完全自定义,灵活 |
| 模型管理 | 自己实现 |
| 适用场景 | 快速验证, 简单部署, 团队 Python 能力强 |
自建推理服务的隐性成本
看起来最简单,但 dynamic batching, 模型热更新, 健康检查, GPU 内存管理这些全要自己写. 适合 PoC 阶段,生产环境建议用 Triton.
方案对比与选型
| 维度 | Triton | TorchServe | FastAPI + ORT |
|---|---|---|---|
| gRPC 支持 | 原生,成熟 | 实验性 | 需自建 |
| Dynamic Batching | 内置,可配置 | 内置 | 需自建 |
| 模型热更新 | 文件目录监听 | Management API | 需自建 |
| GPU 利用率 | 最优(实例分组) | 中等 | 取决于实现 |
| 学习成本 | 中(配置多) | 低 | 低(但后期高) |
| Go 集成友好度 | 高(标准 gRPC) | 中(HTTP 为主) | 中 |
群聊审核场景的推荐方案: Triton Inference Server.
理由:
- 原生 gRPC 接口,Go 客户端直接调用
- Dynamic batching 天然适合群消息的突发流量
- 多模型管理(情感 + 意图 + 毒性)开箱即用
- 模型版本管理内置,A/B 测试只需放两个版本目录
模型服务模式:在线 vs 批量
在线推理 (Online Inference)
请求到达时实时推理,延迟敏感:
|
适用场景:
- 消息发送前拦截(同步审核)
- 实时风险预警
批量推理 (Batch Inference)
积攒一批数据后统一推理,吞吐优先:
|
适用场景:
- 历史消息回扫
- 离线训练数据标注
- 非实时的内容分析报表
混合模式
群聊审核的最佳实践是混合:
|
规则引擎命中率高的场景(如明显脏话)直接拦截,不走模型. 模型处理需要语义理解的灰色地带.
扩容策略
水平扩容
推理服务无状态(模型文件在启动时加载到内存),可以直接水平扩:
|
GPU 实例分组
Triton 支持在同一 GPU 上运行多个模型实例:
|
小模型(DistilBERT 级别, ~200MB)在单张 GPU 上可以跑多个实例并发处理请求.
健康检查与模型版本管理
健康检查
Triton 提供三级健康检查:
| 端点 | 含义 | 用途 |
|---|---|---|
/v2/health/live |
进程存活 | K8s liveness probe |
/v2/health/ready |
模型已加载完成,可以接收请求 | K8s readiness probe |
/v2/models/{name}/ready |
指定模型就绪 | 精细化检查 |
|
模型版本管理
Triton 的版本策略通过 config.pbtxt 配置:
|
上线新版本的流程:
|
版本回滚
如果新版本模型效果下降,只需在 Go 客户端把请求的 model_version 改回旧版本号. 不需要重启推理服务,不需要重新部署. 这是模型版本管理的核心价值.
生产注意事项
模型加载时间
大模型首次加载可能需要 10-30 秒(读文件 + GPU 内存分配 + 图优化). readiness probe 的初始延迟要设够. 如果用 K8s,initialDelaySeconds 至少设 60 秒.
GPU 内存泄漏
某些 ONNX Runtime 版本在长时间运行后会出现 GPU 内存缓慢增长. 建议监控 gpu_memory_used 指标,设置告警阈值. 极端情况下可以定期重启推理容器(滚动更新方式,不中断服务).
模型文件存储
模型文件不要放在容器镜像里(镜像太大,拉取慢). 推荐用 PV/对象存储 + init container 的方式:容器启动时从 S3/MinIO 下载模型到本地,然后启动 Triton.
快速回顾
- 推理服务独立部署: 模型推理和业务逻辑分离,各自扩缩容
- Triton 是首选: 原生 gRPC, dynamic batching, 多模型管理, Go 集成友好
- 在线 + 批量混合: 规则引擎兜底同步拦截,模型处理语义灰区
- 版本管理: 目录结构管理版本,Go 侧指定版本号即可切换/回滚
- 健康检查三级: live(存活) → ready(就绪) → model ready(模型级)
动手练习
- 部署 Triton: 用 Docker 启动 Triton,加载第 15 篇导出的 ONNX 模型,验证
/v2/health/ready返回 200 - 配置 dynamic batching: 修改
config.pbtxt,设置preferred_batch_size和max_queue_delay_microseconds,用压测工具观察 batch 聚合效果 - 模型版本切换: 在模型仓库中放两个版本,分别调用
versions/1和versions/2,验证返回结果不同 - 健康检查集成: 写一个 Go 程序,定期调用 Triton 的 gRPC 健康检查接口,打印模型状态
- 模型热加载: Triton 运行中,新建一个版本目录并放入模型文件,观察 Triton 日志中的自动加载过程