Skip to content

服务化与推理 API

本页速览 服务化(serving)是把模型封装成可调用 API 的关键一跃。本文讲清在线推理服务的本质、HTTP/gRPC 协议选择、输入输出设计、预处理后处理封装,以及模型生命周期管理。

服务化与推理 API

一句话定义:服务化(serving)是把一个离线可推理的模型,封装成一个可供业务方稳定调用的 API 服务的工程过程——让任何客户端通过一次网络请求拿到推理结果,而不用关心模型权重、框架与硬件细节。

行业洞察:"模型能跑"和"模型能被业务用起来"之间隔着整整一层服务化工程。很多团队在笔记本上推理秒出结果,一接线上就翻车:并发一高就超时、请求格式三天两变、模型更新要停服。业界共识是——推理 API 是"产品",模型只是它的内核;接口稳定性、可观测性、生命周期管理占服务化工作量的 70% 以上。这节讲清这 70%。

一、服务化解决什么问题

问题没服务化服务化了
依赖调用方要装 Python、PyTorch、CUDA只发 HTTP/gRPC 请求
并发单进程串行,秒级排队多实例 + 负载均衡 + batching
模型更新调用方改代码重部署服务方灰度替换,调用方无感
资源复用每个业务各自跑一份模型一个服务供多业务共享
可观测黑盒指标、日志、追踪齐备

一句话:服务化让"模型"从研究产物变成可计量的基础设施

二、协议选择:HTTP/REST vs gRPC

维度HTTP/RESTgRPC
数据格式JSON(人类可读、调试友好)Protobuf(二进制、紧凑)
性能序列化开销大,大 payload 明显序列化快 3~10 倍
流式SSE(Server-Sent Events)可行原生双向流,LLM 首选
生态任何语言/工具都能调需生成客户端,工具链略重
调试curl 即调需 grpcurl 等工具
典型场景通用业务 API、网关集成高性能内部服务、LLM 流式

现实中的主流姿势

对外(业务方/网关)用 REST/JSON,对内(模型服务之间、高吞吐路径)用 gRPC。Triton、vLLM 都同时提供两种接口,别选一个用到死。

三、推理 API 设计

1. 输入输出 schema:先定契约,再写代码

一个推荐模型在线推理 API 的典型契约:

json
{
  "model": "ctr-v3",
  "inputs": {
    "user_id": "u_10086",
    "item_ids": ["i_1", "i_2", "i_3"],
    "context": { "scene": "home", "hour": 21 }
  },
  "params": { "top_k": 5 }
}
json
{
  "code": 0,
  "data": {
    "scores": [0.91, 0.82, 0.76, 0.61, 0.44]
  },
  "trace_id": "6f8c..."
}

设计原则:

  1. 输入输出都用稳定的领域语义(如 user_idtop_k),而不是原始张量——业务方不需要懂模型;
  2. 版本化:URL 带版本(/v1/predict)或在 payload 里带 model 字段,别让调用方猜;
  3. 单条优先:在线接口默认单条,批量用专门端点(见下)。

2. 单条 vs 批量

  • 单条:延迟优先,适合实时场景;
  • 批量:同 schema 数组,一次算多个,吞吐优先;
  • 规则:不要两个端点逻辑互相复制,内部都走同一个推理核心,只是外层循环。

3. 流式响应(LLM 必备)

对话类模型的输出是逐 token 生成的(见 大模型推理优化),等全部生成完才返回,首 token 延迟会拖到几十秒。标准做法:

text
HTTP + SSE:
  data: {"delta": "你"}
  data: {"delta": "好"}
  data: {"delta": ","}
  data: {"delta": "世界"}
  data: [DONE]

TTFT(首 token 时间)与 TPOT(每 token 时间)是这类接口的核心指标,压测时别再用"整体延迟"一锅端。

4. 错误码设计

场景建议
参数不合法400 + 明确字段错误信息
模型超时504(网关层)或 408
模型未就绪/过载503 + Retry-After
服务端异常500 + trace_id(方便查日志)

别在错误信息里泄露内部细节

500: cuDNN error in MatMul 这种报错会直接暴露你的框架与 CUDA 版本,给攻击者省事。线上统一"code + 简短 message + trace_id"即可,详见 安全、隐私与合规

四、预处理 / 后处理:必须与训练一致

推理服务里最容易翻车、又最难排查的,就是特征口径漂移:训练时归一化用 (x - mean)/std,线上预处理忘了减 mean;文本分类训练用全角标点清洗,线上没做——模型整体掉点,而模型本身没动过。

三条铁律:

  1. 特征工程代码与训练代码同源:把预处理抽成独立模块,训练和推理共用同一个包,禁止线上"重写一遍";
  2. 版本化:预处理代码随模型版本一起发版,模型回滚时预处理必须同步回滚;
  3. 上线前离线对拍:同一批样本,训练侧预处理输出 vs 线上预处理输出逐字段比对。

更多坑见 常见陷阱与反模式

五、模型生命周期管理

1. 加载策略

text
启动即加载(eager load)     :首请求无延迟,但启动慢(7B 权重加载数十秒)
懒加载(lazy load)          :首请求慢、之后正常,适合低频服务
预热(warmup)               :加载后先跑几个假请求,让 kernel 与内存布局就绪(TensorRT 必需)

生产建议:健康检查通过前必须完成加载 + 预热,否则一扩容,新实例首请求全部超时——这是服务扩容事故的头号原因。

2. 热更新与多版本共存

  • 多版本共存:一个服务实例加载 v1、v2 两版模型,新请求走 v2、存量连接走 v1,平滑切换;
  • 灰度:先放 5% 流量到新版本,看监控再全量(详见 发布策略:灰度与回滚);
  • 模型与代码解耦:模型文件放独立存储(如对象存储 + 哈希寻址),模型更新不动服务代码。

六、并发、调度与请求队列

在线服务天然是排队系统

text
请求 ──► 接收 ──► 限流(可选) ──► 排队队列 ──► 调度器(动态 batching) ──► GPU 推理 ──► 响应
  • 队列有界:无界队列在流量尖峰时会攒几百万请求,恢复时间被拖死;设上限,超了直接 503。
  • 动态 batching:调度器把 10ms 窗口内到达的请求凑成一个 batch 一起算,吞吐翻倍而延迟只增几毫秒——这是在线服务最核心的调度技巧,详见 性能优化与容量规划
  • 并发模型:异步非阻塞 + 多实例,别用"每请求一个线程 + GIL 内 Python 计算"。

七、服务框架盘点

框架一句话定位适合谁
FastAPI轻量 Python API 框架,自己管预处理/编排中小团队、快速上线
TorchServePyTorch 官方,模型打包/多版本开箱即用PyTorch 生态、要官方支持
Triton Inference Server多框架多模型高吞吐,动态 batching 一等公民生产级、混合模型
KServeKubernetes 原生,Serverless 扩缩容云原生 K8s 团队
BentoML把模型打包成标准制品(Bento)需要工程化打包流程
Ray Serve分布式 + 灵活编排复杂服务、多模型组合

框架深度对比见 框架与平台怎么选;FastAPI 与 Triton 的实战见 FastAPI + Docker 在线服务NVIDIA Triton 多模型服务

八、可靠性:超时、重试、限流、熔断

机制作用经验值
超时(timeout)防止调用方无限等待设为模型 P99 延迟的 3~5 倍
重试(retry)容忍瞬时失败指数退避 + 抖动;最多 2~3 次
限流(rate limit)保护后端不被打爆令牌桶;对单 key/单 IP 均限
熔断(circuit breaker)后端故障时快速失败连续错误率 > 50% 断开 30s

重试放大(retry storm)

上游超时重试 + 下游恰好过载 = 雪崩。必须限制重试次数与并发,并给不同调用方设不同配额,避免一次故障拖垮整个集群。

九、健康检查与就绪探针

  • 存活探针(liveness):进程活着就行;
  • 就绪探针(readiness):模型加载完毕 + 预热完成 + 能处理请求才算就绪,否则从负载均衡摘除;
  • 启动探针(startup):给慢加载(如 7B 模型)留出加载时间,避免就绪探针误杀。

在 K8s 里的配置示例:

yaml
readinessProbe:
  httpGet: { path: /health/ready, port: 8000 }
  initialDelaySeconds: 60   # 等模型加载完成
  periodSeconds: 10
livenessProbe:
  httpGet: { path: /health/live, port: 8000 }
  periodSeconds: 30

权衡与取舍

决策点选项怎么选
REST vs gRPC通用 vs 高性能对外 REST,内部高吞吐 gRPC
框架轻量 FastAPI vs 重型 Triton单模型简单场景 FastAPI;多模型高吞吐 Triton
加载方式eager vs lazy生产 eager + 预热;低频服务 lazy
超时重试宽松 vs 严格严格限制,防雪崩优先
队列有界 vs 无界永远有界,超限即 503

一句话总结:服务化是把模型"产品化"——先定契约(API),再保稳定(超时/限流/重试),最后管好生命周期(加载/预热/灰度),模型本身反而是最简单的一环

延伸阅读

参考资料