大模型接口偶尔会拒绝请求并提示频率超限,拦在前面的是限流,而限流的核心动作就是计数,令牌桶是最常见也最耐用的数法。计数方式选错,要么拦不住洪峰,要么误伤正常用户。
固定窗口数错的坑
最朴素的数法是固定窗口:每分钟清零一次计数器,超过上限就拒绝。进来的是请求流,发生的是按窗口累加,出去的是放行或拒绝。
它的毛病在窗口交界:前一分钟末尾挤进来一整批,后一分钟开头又放一整批,两批只隔一秒,合计却是双倍配额。计数器本身没数错,错的是窗口的切法,交界处的漏洞怎么调阈值都堵不严。
固定窗口并非一无是处:实现简单、内存开销小,对精度要求不高的内部接口还在用它。选不选它,看的是业务能不能容忍交界处的双倍突刺,能容忍就省下复杂度。
令牌桶是怎么漏的
令牌桶换了思路:系统按固定速率往桶里放令牌,桶有容量上限,每个请求取走一枚才放行,桶空了就等或拒。进来的是匀速生成的令牌,发生的是取用与回填,出去的是按令牌放行的请求。
桶的容量就是突发余量:平时匀速放,闲时令牌攒在桶里,忙时一口气取出来,短突发就接住了。平均速率由放令牌的速度决定,突发上限由桶深决定,两个旋钮各管一头,互不干扰。两个旋钮怎么配也有经验值:桶深约等于一两秒的匀速放量,够挡住网络重传和用户手抖,不至于放行真正的洪峰。放令牌的节奏也有变体:有的按固定间隔补一批,有的按时间比例连续补,效果差别不大,回收粒度不同。桶还要防久置失效:服务重启后桶里该有多少令牌,按停机时长折算,别让重启变成一次免费的大突发。
两个旋钮加两处细节,配置时对着看。
| 配置项 | 管的是什么 | 文中给的取值参考 |
|---|---|---|
| 放令牌速率 | 平均速率 | 由放令牌的速度决定,令牌匀速生成 |
| 桶深 | 突发上限 | 约等于一两秒的匀速放量,够挡网络重传和用户手抖 |
| 补令牌方式 | 回收粒度 | 按固定间隔补一批,或按时间比例连续补,效果差别不大 |
| 重启折算 | 停机期间的令牌 | 按停机时长折算,别让重启变成一次免费的大突发 |
你在 DeepSeek、Kimi 的开放接口文档里看到的每分钟请求数加每分钟标记数,多半就是令牌桶或它的变体在起作用。文档里通常还会写明超限后的错误码,接入前先看一眼,免得上线后对着报错猜。
超了之后请求去哪
被拦的请求并非直接扔掉:有的排队等下一批令牌,有的返回带等待时长的提示,让调用方按退避节奏再试。进来的是超额请求,发生的是分流处置,出去的是排队单或明确的拒绝响应。
限流还常常分层:账号级、接口级、全局级各一道,过三关才到模型。层级越多越稳,但误伤也越难排查,日志里要把哪一层拦的记清楚,否则调用方查半天找不到原因。计数的数据结构也要抠:内存里一个带时间戳的计数器就够,别为限流上数据库,查库的延迟比限流本身还贵。分布式限流常用集中计数加本地预扣,各实例先领一批额度用完再领,减少对中心存储的冲击。再配一层监控,拦得多不多、拦得对不对,一目了然。
限流参数要不要对用户公开也有讲究:公开了调用方能主动配合,藏起来则减少被刻意顶格利用的风险,常见折中是公开额度范围、不公开精确桶深。
- 速查:固定窗口怕交界双倍;令牌桶用速率加桶深两个旋钮;桶深管突发;分层限流各挡一道;超额请求排队或提示退避。
限流挡在外面,里面的调度还有优先级队列是怎么排 VIP 的?分等级出队、自动扩缩容是怎么加减机器的?每一步都讲透、批大小和延迟的取舍是怎么权衡的?和温度设 0 为什么输出还不完全一样?。
换个问法再问限流算法三句
- 令牌桶和漏桶差在哪?:漏桶强制出口匀速,令牌桶允许攒出来的突发,前者稳后者灵活,按业务形状选。
- 桶满了令牌会怎样?:直接丢弃,桶顶就是突发上限,不会积压到桶外。
- 多实例下计数放哪?:要用共享存储集中计数,各数各的等于没限,总量早就超了。中心计数挂了要放行还是拒绝,提前定好降级预案。


