文章

LLM Serving 过载保护生产实战:用 Queue SLO、Backpressure 与弹性扩容守住尾延迟

面向生产级大模型推理服务,系统讲解如何用队列等待时间、TTFT SLO、Backpressure 与弹性扩容协同应对突发流量,避免扩容滞后、尾延迟失控和无限排队,并给出关键指标、参数配置、上线检查清单与常见误区。

LLM Serving 过载保护生产实战:用 Queue SLO、Backpressure 与弹性扩容守住尾延迟

LLM Serving 过载保护生产实战:用 Queue SLO、Backpressure 与弹性扩容守住尾延迟

生产级大模型推理服务最常见的故障,往往不是 GPU 直接 OOM,而是流量突然上升后,已有副本满载、请求持续进入等待队列,Autoscaler 观察到压力才开始申请 GPU、拉起容器、加载模型。等新副本真正 Ready 时,前面那批请求的 TTFT(Time To First Token) 早已超出 SLO。

这类问题的本质是一个时间差:排队恶化的速度通常快于 GPU 副本扩容的速度

因此,LLM Serving 的容量治理不能只有 Autoscaling,还需要三层协同:

  1. SLO 感知:判断队列是否正在消耗用户可接受的等待预算;
  2. Backpressure / Load Shedding:在系统已无法按 SLO 消化新增请求时停止无限排队;
  3. Autoscaling:增加长期可用吞吐,修复供需缺口。

一句话概括:扩容负责”增加供给”,Backpressure 负责”限制过量需求”,SLO 负责告诉系统”什么时候必须行动”。

核心原理:从 Queue Length 升级到 Queue SLO

1. 不要把 GPU 利用率当成唯一扩容信号

传统 Web 服务习惯按 CPU 或内存利用率扩容,但 LLM 推理有几个特殊点:请求长度差异大、Prefill 与 Decode 负载形态不同、并发调度会改变单请求延迟,而且 GPU 达到较高利用率时未必意味着 SLO 已经恶化。

更直接的生产信号通常包括:

  • vllm:num_requests_running:当前正在执行的请求数;
  • vllm:num_requests_waiting:等待调度的请求数;
  • vllm:request_queue_time_seconds:请求实际排队时间;
  • vllm:time_to_first_token_seconds:TTFT;
  • vllm:e2e_request_latency_seconds:端到端延迟。

vLLM 的生产指标已经直接暴露这些维度,因此 Autoscaler 不必只盯 GPU utilization。

2. Queue Length 是容量信号,Queue Time 才更接近用户体验

假设同样有 20 个等待请求:

  • 模型 A 每秒能消化 10 个请求;
  • 模型 B 每秒只能消化 1 个请求。

二者的 queue length 相同,但用户等待风险完全不同。所以更合理的控制逻辑应该关心:

预计等待时间 + 当前排队时间 + 新副本冷启动时间,是否仍在 TTFT SLO 预算内。

可以把它抽象成一个简单判断:

remaining_slo_budget = ttft_slo - current_queue_time

if estimated_scale_up_time > remaining_slo_budget:
    start_backpressure()
else:
    scale_out()

这里不是要求生产系统精确预测每个请求,而是建立一条基本原则:如果扩容来不及救当前请求,就不要继续把请求塞进无限队列。

3. Autoscaling 与 Backpressure 必须同时存在

Ray Serve 提供了两个很关键但语义不同的参数:

  • max_ongoing_requests:限制每个副本同时接收的请求数量;
  • max_queued_requests:限制代理或调用端继续排队的请求数量。

达到 max_queued_requests 后,可以直接产生 Backpressure,并返回 HTTP 503;通过 BackpressureConfig 还可以配置为 429 并带 Retry-After

这比”所有请求都先接收,然后慢慢排队”更适合严格 SLO 的在线推理服务——因为无限队列只是在把容量不足隐藏成尾延迟问题。

工程落地:四层控制闭环

第一层:建立请求级 SLO

先按业务类型给请求分类,不要全站只有一个统一超时时间。例如:

请求类型关注指标建议治理方式
在线对话TTFT P95 / P99、TPOT保留 warm pool,快速扩容,严格限队列
Agent 工具调用端到端延迟、TTFT中等队列,允许有限重试
后台生成任务完成时间可进入异步队列,优先成本效率
Batch 推理作业完成时间不应与交互请求竞争同一 SLO

这里的关键不是具体阈值,而是 Interactive 和 Batch 必须采用不同的容量策略

第二层:用 waiting、queue time 和 TTFT 建立告警

最低限度建议建立以下指标:

  • P95 / P99 TTFT
  • P95 queue_time
  • num_requests_waiting / num_requests_running
  • request_reject_rate
  • replica_ready_latency
  • GPU replica count

其中 replica_ready_latency 很容易被忽略。它应该覆盖从 Autoscaler 决定扩容到 GPU Pod 可真正接受请求的完整时间,包括调度、镜像启动、模型权重加载和初始化。如果模型需要几十秒甚至数分钟才能 Ready,那么只把扩容观察周期调得更快并不能解决问题。

第三层:Autoscaler 以”每副本持续压力”扩容

Ray Serve 的 Autoscaler 使用 target_ongoing_requests 作为每副本希望维持的平均 ongoing request 数,并支持 upscale_delay_sdownscale_delay_smin_replicasmax_replicas 等参数。

KServe 则可以通过 KEDA 使用 Prometheus 或 OpenTelemetry 指标驱动扩容,其官方示例直接使用 vLLM 的 vllm:num_requests_running 作为扩容指标。

生产上不建议一开始就追求复杂预测模型,更稳妥的步骤是:

  1. 压测得到单副本在目标 TTFT 下的安全并发;
  2. 把 Autoscaler target 设置在这个安全并发以下;
  3. 测量实际 scale-up latency;
  4. 根据 scale-up latency 决定需要多少 minReplicas 或 warm pool;
  5. 再根据历史流量评估是否值得加入预测式扩容。

第四层:超载时主动拒绝,而不是无限排队

建议把系统状态分成三个区间:

状态触发条件动作
GREENqueue_time < 40% TTFT budget正常接收
YELLOWqueue_time >= 40% TTFT budget 且 waiting requests 持续增长触发快速扩容,降低非关键流量优先级
REDqueue_time + estimated_scale_up_time >= TTFT budgetBackpressure / load shedding,返回 429 或 503 + Retry-After

阈值需要通过压测校准,不应该机械照搬。但三段式状态机比单纯”超过并发就扩容”更容易解释和运营。

一个容易忽略的新方向:扩容单位不一定永远是”整模型副本”

2026 年 8 月发布的 OpScale 讨论了一个更细的研究方向:传统 Autoscaling 把完整模型副本作为最小扩容单位,但不同算子在运行时的资源弹性并不完全一致,因此可以探索 Operator-level Provisioning and Autoscaling

这个方向目前更适合作为前沿架构研究,而不是普通团队直接照搬的生产方案,但它提醒了一个重要事实:LLM 推理扩容的成本和响应速度,受”扩容粒度”强烈影响。

现阶段大多数团队更实际的选择仍然是整副本扩容 + warm pool + Backpressure;只有在 GPU 集群规模足够大、SLO 非常严格、资源成本足够敏感时,才值得进一步研究更细粒度的弹性机制。

适用场景

这套方案尤其适合以下业务:

  • 面向公网、流量波动明显的 Chat / Copilot 服务;
  • 企业内部共享的大模型网关;
  • 多租户 GPU 推理平台;
  • 需要严格 TTFT 的智能客服、搜索问答;
  • 使用 KServe、Ray Serve、vLLM 等构建的 Kubernetes 推理服务。

如果业务本身是离线 Batch,用户不关心秒级 TTFT,那么优先级应转向 GPU 利用率和单位 Token 成本,而不是强行套用在线过载保护逻辑。

常见误区

误区一:GPU 还有余量,所以服务没有过载

LLM 服务是否过载应以 SLO 是否持续可满足 判断,而不是只看 GPU utilization。队列已经快速增长时,即使 GPU 指标还没到 100%,用户体验也可能已经开始恶化。

误区二:只要扩容够快,就不需要限流

扩容存在物理延迟。GPU 调度、镜像启动和模型加载都不可能是零时间。突发流量足够大时,任何 Autoscaler 都需要 Backpressure 做保护。

误区三:把 max queue 设置得越大越安全

大队列不会创造吞吐,只会把失败从”立即拒绝”推迟成”等待很久后超时”。对于交互业务,过大的队列通常意味着更差的 P99。

误区四:minReplicas=0 一定最省钱

Scale-to-zero 可以省空闲 GPU,但会引入冷启动。若模型权重加载时间接近甚至超过用户 TTFT SLO,那么 scale-to-zero 本身就与交互服务目标冲突。

上线检查清单

上线前至少完成以下检查:

  • 已定义交互请求的 TTFT P95 / P99 SLO;
  • 已压测得到单副本在目标 SLO 下的安全并发;
  • 已采集 num_requests_runningnum_requests_waiting、queue time、TTFT;
  • 已测量从扩容决策到新副本 Ready 的真实时间;
  • 已设置合理的 minReplicas 或 warm pool;
  • 已限制最大排队请求数;
  • 已定义 429/503 与 Retry-After 策略;
  • 客户端已实现指数退避,并限制最大重试次数;
  • 已验证突发流量下拒绝率上升时,现有请求的 P99 不会一起失控;
  • 已验证降容不会过快导致反复冷启动;
  • 已区分在线和 Batch 请求的容量池或优先级。

FAQ

LLM Serving 为什么不能只按 GPU 利用率做自动扩缩容?

因为 GPU 利用率不能直接表达请求排队多久,也无法表达剩余 TTFT 预算。生产系统至少需要联合 waiting requests、queue time、TTFT 和 replica startup latency。

Backpressure 应返回 429 还是 503?

如果语义是”当前租户或调用方请求过多,稍后重试”,429 更容易让客户端理解;如果语义是”服务整体暂时不可用或容量不足”,503 更自然。Ray Serve 的 Backpressure 配置支持这两种状态码,并可附带 Retry-After

Autoscaler 的目标并发应该怎样确定?

不要从经验拍值。先固定模型、GPU、输入输出长度分布和推理参数做负载测试,以目标 TTFT/TPOT 为约束找到单副本安全并发,再把 Autoscaler target 设置在该边界以内,并留出突发余量。

参考资料

  1. Ray Serve Autoscaling Guide — https://docs.ray.io/en/latest/serve/autoscaling-guide.html
  2. Ray Serve Advanced Autoscaling — https://docs.ray.io/en/latest/serve/advanced-guides/advanced-autoscaling.html
  3. Ray Serve Production Best Practices — Load Shedding — https://docs.ray.io/en/master/serve/production-guide/best-practices.html
  4. vLLM Production Metrics — https://docs.vllm.ai/en/latest/usage/metrics/
  5. KServe — Autoscale InferenceService with LLM Metrics — https://kserve.github.io/website/docs/model-serving/generative-inference/autoscaling
  6. OpScale: Operator-level Provisioning and Autoscaling for LLM Serving — https://arxiv.org/abs/2608.13499

常见问题

LLM Serving 为什么不能只按 GPU 利用率做自动扩缩容?
GPU 利用率是滞后指标,无法表达请求已经排队多久、还剩多少 TTFT 预算。生产环境应联合观察等待请求数、queue time、TTFT 以及副本冷启动时间,再决定扩容与限流。
Backpressure 会不会降低系统吞吐?
合理配置的 Backpressure 不是为了主动降吞吐,而是阻止队列无限增长。超过系统可恢复范围后及时返回 429 或 503,通常比让请求长时间排队并最终超时更稳定。
Autoscaler 的目标并发应该怎样确定?
不要凭经验拍值,应先固定模型、GPU、输入输出长度分布与推理参数做负载测试,以目标 TTFT/TPOT 为约束找到单副本安全并发,再把 target 设置在该边界以内并预留突发余量。