- 瞬时洪峰,而不是持续高并发
例如平时只有 10~20 并发,但某个定时任务、会议结束、批处理任务同时触发:
10:00:00 12个请求
10:00:01 300个请求
10:00:02 20个请求
平均 QPS 看起来完全正常,但 1 秒内模型直接被打满。
这种情况尤其容易出现在企业系统:
- 定时任务整点触发
- 前端用户同时点击
- 上游批处理一次性提交
- 某服务恢复后积压任务集中释放
所以平均 QPS 很容易骗人,真正应该压的是 burst capacity。
- 请求数量没增加,但请求突然变“重”了
这是 LLM 最特殊的问题之一。
平时:
32并发
平均输入 2K token
平均输出 500 token
突然某一批:
32并发
平均输入 25K token
平均输出 4K token
从系统层看:
并发还是32
QPS也差不多
但 GPU 负载完全不是一个量级。
于是会出现一种很诡异的现象:
明明没有超过并发阈值,TTFT 却突然从 1 秒涨到 20 秒。
所以企业模型流量最大的问题之一就是:
请求不是等价请求。
- 一个超长请求拖死一批短请求
比如:
A:输入 100K token
B:输入 500 token
C:输入 800 token
D:输入 1K token
如果调度设计不好,很容易出现 Head-of-Line Blocking:
A进入prefill
↓
GPU大量时间处理A
↓
B/C/D虽然很轻
却一起变慢
这就是为什么仅仅做 FIFO 队列经常不够。
实际可能需要:
短请求队列
普通请求队列
长上下文请求队列
甚至长上下文业务单独部署模型实例。
- 大量请求同时被取消,但模型还在继续算
这是非常容易漏掉的。
例如用户:
请求模型
↓
等10秒
↓
关闭页面
或者上游:
30秒HTTP timeout
但下游 vLLM 可能还在:
继续生成
继续占KV Cache
继续占GPU
于是出现:
用户已经不要结果了
GPU还在给用户算
如果大量发生,会变成幽灵负载。
所以流式调用尤其要保证:
客户端断开
↓
Gateway感知
↓
取消下游generation
↓
释放资源
- 重试风暴
这是最危险的一类。
比如模型偶尔慢了一点:
请求超时
↓
业务系统自动retry
↓
Gateway自动retry
↓
SDK自己也retry
一个请求可能瞬间变成:
1
↓
2
↓
4
↓
8
结果是:
模型只是暂时变慢,最后被重试彻底打死。
尤其企业系统经常多层都有 retry:
前端
↓
业务服务
↓
AI Gateway
↓
模型SDK
每层都以为自己“重试一次没关系”。
组合起来就很恐怖。
- 模型刚恢复,所有流量一起冲回来
和重试风暴类似,但这是 Thundering Herd。
例如模型挂了 30 秒:
1000个请求排队
模型恢复:
健康检查变绿
↓
1000请求一起释放
↓
模型再次挂掉
于是:
恢复
→ 打爆
→ 恢复
→ 打爆
这种震荡现场非常常见。
恢复流量一定要:
慢启动
例如:
10%
↓
30%
↓
50%
↓
100%
而不是健康检查一通过就全量放流。
- 副本数量一样,但压力完全不均衡
假设部署了:
Model-01
Model-02
Model-03
Model-04
理论上负载均衡。
但实际可能:
01:大量长上下文
02:大量短请求
03:刚好几个超长生成
04:比较空闲
如果只是 Round Robin:
每台请求数量都是10个
看起来“非常均衡”。
但实际上:
01 KV Cache 95%
04 KV Cache 30%
所以 LLM 做负载均衡不能只看:
request_count
最好逐渐变成:
running request
+
waiting request
+
KV Cache
+
token workload
- 模型没死,但进入“半死状态”
这个比完全挂掉更麻烦。
例如:
HTTP还能连
health接口返回200
GPU进程也还活着
但是:
TTFT:40秒
TPS:从50降到3
大量请求卡住
传统健康检查会认为:
服务正常。
实际上已经不能用了。
所以模型健康状态不能只判断:
GET /health = 200
而要有业务健康指标:
P95 TTFT
P99 latency
TPS
queue time
error rate
KV Cache
例如:
health = 200
但
P95 TTFT > 15s
→ 视为 degraded
- 显存碎片 / KV Cache 导致突然 OOM
这种情况也很烦,因为:
前一分钟正常
下一分钟突然OOM
并不一定是请求数量增加。
可能只是:
长短请求混合
上下文长度变化
KV Cache占用变化
显存碎片
于是:
GPU utilization = 70%
你以为还有空间,
结果:
新请求prefill
→ CUDA OOM
所以不能单纯拿:
GPU利用率
判断还能不能接流量。
- 某个业务突然改变 Prompt,整体容量直接下降
这个非常企业化。
比如原来的某 Agent:
System Prompt:2K
知识库:3K
用户输入:1K
某天产品把 Prompt 改成:
System Prompt:8K
知识库:10K
历史对话:15K
业务人数没变,QPS 没变,模型配置也没变。
但模型吞吐能力可能直接下降一大截。
于是运维看到:
“今天也没什么流量,模型怎么突然变慢?”
实际上是单请求 Token 成本发生变化。
所以模型流量治理必须和 Prompt / RAG / Agent 链路关联起来。
- Agent 会产生内部流量放大
这个以后你做 Agent 平台一定会碰到。
用户发一个请求:
用户请求 = 1
但 Agent 内部可能:
意图识别 1次LLM
问题改写 1次LLM
Planner 1次LLM
Tool结果总结 1次LLM
反思 1次LLM
最终生成 1次LLM
实际上:
1个用户请求
≈ 6个模型请求
Multi-Agent 更夸张:
1个用户请求
→ 20~50次内部LLM调用
这就是所谓:
Traffic Amplification,流量放大。
所以以后不能只限:
用户QPS
还要限制:
一次任务最多允许多少LLM调用
最多多少token
最大Agent Loop次数
否则一个 Agent 就可能吃掉整个模型资源池。
- Streaming 导致连接资源先耗尽
模型生成天然长连接。
传统 API:
1000 QPS
每个请求50ms
连接很快释放。
LLM:
50 QPS
每个请求持续30秒
就可能长期保持:
1500个HTTP/SSE连接
所以你可能 GPU 还没满:
GPU 60%
结果:
Gateway连接池满了
Nginx连接满了
FD耗尽
表现出来却还是:
“模型接口挂了。”
实际上压根不是模型问题。
- 上游速度比下游消费速度快
例如模型疯狂 streaming:
100 token/s
但是:
Gateway
WebSocket
前端
网络
只能消费:
20 token/s
就会形成:
模型
↓
Gateway Buffer越来越大
↓
内存上涨
↓
OOM
这是典型的 Backpressure 传播问题。
所以流控不只要控制:
请求能不能进模型。
还要控制:
模型输出之后,下游能不能吃得下。
