给大模型加流控:为什么 QPS 不是越限越安全
很多团队把"QPS 限额"当成一个大数往下调,直到业务告警就再调低一点。但网关上的速率限制其实有个更微妙的角色:它是你和模型推理集群之间唯一的缓冲带。今天的主题就是这个缓冲带该怎么设,很多人第一直觉的答案是错的。

给大模型加流控:为什么 QPS 不是越限越安全
很多团队把"QPS 限额"当成一个大数往下调,直到业务告警就再调低一点。但网关上的速率限制其实有个更微妙的角色:它是你和模型推理集群之间唯一的缓冲带。今天的主题就是这个缓冲带该怎么设,很多人第一直觉的答案是错的。
先说结论:**限制值的正确位置,取决于你的失败成本结构,而不是预估流量峰值。**
网关限流到底在防什么
模型服务是这类系统里最贵的下游:一次推理请求要占 GPU 显存几十秒到几分钟,请求堆积会直接导致 TTFT(首字延迟)劣化。网关如果不限速,突发流量进来时,GPU 队列会迅速堆满——不是服务崩溃,而是所有请求的响应时间一起拖尾,用户体验是"全站变慢"。
所以限流的真正目标不是防过载,而是**把过载变成可以预测的排队行为**。这一点和传统 API 限流很不一样。
三种常见设限方式,各自的坑
**固定值 QPS 上限**:最简单,但问题是你用一个常数去压一个分布随时间剧烈变化的下游。早高峰 QPS 是 200,夜里只有 15,同一个 100 上限在两个时段意味着完全不同的排队长度。
**窗口计数器(每 10 秒放行 N 个)**:比裸 QPS 稍微稳一点,但边界效应严重——第 10 秒刚开始的瞬间,两次窗口之间可能瞬间放行 2N 个请求。模型服务对秒级突发极敏感,这种尖峰正是你想避免的。
**令牌桶(token bucket)**:维护一个桶,以恒定速率往里填令牌,每次请求消耗一个。相比窗口计数器,它天然允许适度的突发(桶里有富裕令牌),但不会让突发量无上限。大多数生产网关用这个。
参数怎么定:从推理耗时反推
这两个数是网关侧必须调的:
- **填充速率(rps)**:单位时间补充的令牌数。
- **桶容量(burst)**:突发时一次性能放过的请求数。
正确做法是从下游反推,而不是拍脑袋。你需要的数据是:
- 模型推理的平均延迟 p95(不是平均数,尾延迟决定排队行为)
- 后端 GPU 池的并发处理上限
一个粗略但实用的推导:如果你的并发上限是 C,平均延迟是 T 秒,那么系统能稳定承接的 rps 大约是 C / T。把 gateway rps 设在 0.7 到 0.8 倍这个值,留出缓冲空间。
**桶容量**则是另一个权衡。太大,突发的尖峰还是能打到下游;太小,正常波动也会被误伤。经验起点是 C 的 1.2 到 1.5 倍——够容纳合理的不规则流量,又不至于把欠饱和状态拉成真正的堆积。
那个反直觉的地方
很多人设限后的第一反应是:"限流比例(被拒绝的请求占比)要是 0%,就说明我限得不够严。" 这是最容易踩的误区。
**零拒绝率通常说明你有大量空闲容量在浪费钱。** 而且,一个长期几乎不触发限流的系统,你根本不知道限流真正被压到时行为是什么样的——下一次流量翻倍,你只在生产环境第一次见到悬崖。
更扎实的做法是:故意在预发环境送一个比你预估峰值高 50% 的负载,观察 TTFT 的劣化曲线在哪里开始变陡,那个拐点附近的 QPS 才值得作为你的基准上下限。
一个检查清单
发布限流规则之前,过一遍这三条:
1. 你的 rps 和桶容量是从 p95 延迟反推的,还是运营拍的一个数?
2. 限流触发时的行为是什么——直接 429,还是让它在队列里等待并有超时?(直接 429 的误伤率通常比排队 + 等待高不少。)
3. 你有没有一个办法,能在不写代码的情况下看到"距离最近一次 429 还有多少余量"?看不到这个数,下一次扩容就是你被动响应的时刻。
流控不是惩罚用户的机制,是你让成本可预测的手段。用反推的数设限,用观测数据校准,比"先设高一点,出事再降"安全得多。
留言区
欢迎分享你的想法!
加载留言中…