api接口被限制次数解决方法

接入与开发

接口被限流后怎么定位是哪一层卡的,再按排队、分批、退避、多通道几条路子把请求量稳下来。

本地跑得好好的脚本,一上量就返回限流或超时,这时候最怕的是瞎重试——越重试越黑。这篇不讲协议也不讲客户端怎么配,只给一套从日志定位到处置顺序的排查流程,以及几种能长期用下去的降速与分流做法。

本地跑得好好的脚本,一上量就返回限流或超时,这时候最怕的是瞎重试——越重试越黑。很多人第一反应是换个 Key,或者把 sleep 调大,结果问题还是原样复现。其实限流不是一个单点故障,它可能发生在账号、Key、通道、机器出口这几层里的任何一层,处置方法完全不同。先定位再动手,比在代码里乱加重试划算得多。

这篇不讲协议也不讲客户端怎么配,只给一套从日志定位到处置顺序的排查流程,以及几种能长期用下去的降速与分流做法。你不需要理解底层实现,只要照着下面几张表和一个排查顺序走一遍,就能把大部分限流问题收敛到可以管理的范围。前提是你愿意花半小时把日志字段补齐,这半小时比后面一周瞎试都值。

先判断限的是哪一层,别急着改代码

限流最容易被误判的地方,是把它当成一个整体问题。实际上它至少来自四个不同的位置:官方账号级配额、单个 Key 的每分钟请求数、中转站自己的通道并发限制,以及你自己机器的出口带宽或连接池上限。这四层的表现可能都是「返回一个限制类错误」,但解决路径完全不一样。先分清来源,后面所有动作才有意义。

限流来源典型现象怎么验证
官方账号级配额同一账号下换任意 Key 都复现,且长时间不恢复换 Key 后仍失败,基本可确认
单 Key 每分钟请求数换一个 Key 立刻好转,原 Key 过一会儿又能用同一代码换 Key 对照测试
中转站通道并发错误里出现通道或队列相关字样,换站点可能消失换通道复现,换站点不出现
本机出口或连接池并发一高就超时,机器负载同时升高看本机连接数与带宽占用

看响应头和错误体里的关键字,是判断配额类还是时序类最快的方式。配额类通常带一个明确的配额或额度字样,时序类则更多出现在短时间密集请求之后,随后自行缓解。把错误信息比对接口报错类型页面,能省掉大量猜测。同一份代码换一个 Key 或换一个通道还复现,基本可以排除账号配额问题,往通道或本地找。

  • 先记录错误原文,不要只看状态码,错误体里的关键字决定方向
  • 同一份代码分别用两个 Key 跑,看是否只有一个失败
  • 换一个通道重试同一批请求,确认是不是站点侧的问题
  • 把并发降到最低再跑一遍,如果好了说明是本地或时序问题,而不是配额被打满

日志里必须有的几个字段,不然根本查不动

限流排查里最尴尬的情况是:现象有,但日志里只有一行「请求失败」。没有时间戳、没有 Key 尾号、没有模型标识,你连失败集中在哪个时间段都看不出来,更别说量出阈值。这不是优化问题,是信息缺失问题。缺字段的日志等于没日志,先补齐再谈别的。

把失败请求按分钟聚合,是这一步的核心动作。聚合之后你会看到两种典型图形:一种是均匀散布的失败,说明是持续性的额度不足或者一直压在上限附近;另一种是集中在一个瞬间的尖峰,那通常是你自己的并发设计造成的,比如循环里没有节流、多个任务同时启动。两种图形的处置方向完全不同。

  • 时间戳精确到毫秒,不要只用秒,否则看不出同秒内的并发爆炸
  • Key 尾号,避免记录完整密钥,但能区分是哪个 Key 在报错
  • 目标模型或调用目标,不同目标的额度口径不一样
  • 单次耗时与状态码,用于区分限流、超时和鉴权失败
  • 重试次数与请求体大小,重试次数能暴露隐含的放大效应

把限流阈值量出来,别猜

很多团队处理限流的方式是靠感觉调 sleep,感觉不够就再加一点。这样做的问题是,你永远不知道离上限还有多远,也没法判断加量之后会不会再撞线。正确做法是用固定并发慢慢加压,找到第一个开始失败的点,记下当时的每分钟请求数和并发数。这个点就是你的实际可用阈值,不是一个理论值。

加压的过程要有阶梯,不要一口气拉满。每一档跑够一个观察窗口,稳定不失败再进下一档。如果延迟本身就高,并发数看着不高也可能触发限流,因为单位时间内的请求完成率上不去。建议先用接口测速页面拿一条延迟基线,把延迟和并发一起看。注意不同模型、不同档位的额度不是同一个数,去官方 API 价格页和厂商页面核对当时的口径,别拿一个模型的阈值套到另一个上。

观察项低档位表现高档位表现说明
失败率接近零开始出现零星失败第一个失败点就是实际阈值
平均延迟平稳逐渐抬高延迟上升会放大限流概率
重试次数为零被动上升重试会进一步推高请求量
吞吐量线性增长趋于平台甚至回落平台期说明已经压到上限

退避与排队:让失败变成慢,而不是变成错

限流出现之后的第一个技术动作,应该是让请求变慢,而不是让它变错。指数退避是最常见的做法,但很多人漏掉了一个细节:退避必须加抖动。如果所有客户端都按同样的倍数退避,它们会在同一秒一起重试,结果是把一次短暂的限流放大成持续的限流。加上随机抖动之后,重试时间点被摊开,系统才真正有机会恢复。

另一个细节是重试范围。只有幂等请求才适合直接重试,写类操作如果重复执行会带来副作用,要么带上幂等键,要么干脆不重试、直接报给上层处理。这一点和 Agent 调 API 的幂等要求是同一个逻辑。至于排队,建议把「同时在飞的请求数」放到本地排队器里统一限制,而不是在业务代码里到处 sleep,前者好维护、好观察、好调参。

  1. 限制在飞请求数用一个信号量或本地队列把并发封在阈值以下,所有调用统一走这个入口,验证方式是观察在飞数量是否稳定不超设上限。
  2. 失败分类把限流、超时、鉴权、业务错误分开处理,只对限流和超时进入退避流程,验证方式看日志里各类型是否被正确标记。
  3. 加抖动退避基础退避时间乘以随机因子,避免同一秒集体重试,验证方式是看重试时间点是否被摊开而不是聚成一簇。
  4. 设重试上限每个请求最多重试固定次数,超过就报错,验证方式是日志里不再出现无限重试的长尾记录。
退避的目标不是把请求发出去,而是让系统在不崩溃的前提下把该发的请求发完。

把请求量压下去:合并、分批、缓存三招

退避和排队解决的是「怎么慢下来」,但慢下来之后总吞吐还是会受限,尤其是任务量本来就大的场景。这时候要从源头压请求数。能合批的合批,一次请求里塞多条内容,通常比发多条并发请求更省额度,因为很多限流是按请求次数算的,不是按内容量算的。这一招对批量评测、批量转写这类任务效果最直接。

不能合并的就分批跑,批与批之间留间隔,失败批单独重投,别整批重跑。整批重跑会把已经成功的那部分再算一遍,既浪费时间也浪费额度。第三招是缓存,对重复问法做结果缓存,尤其是在评测、回归、多环境重复跑的场景里,同一批输入往往会被反复提交,缓存命中之后请求量能明显下降。

  • 先统计有多少请求是重复输入,重复比例高就先做缓存而不是调并发
  • 能合成一条请求的多个小任务,合并后再发
  • 分批时记录每批的边界与结果,失败只重投失败批
  • 给缓存设过期策略,避免长期返回旧结果

多通道与降级:一条路走不通先切路

单靠一个通道硬扛,遇到通道侧限流时只能等。比较稳的做法是准备备用 Key 或备用通道,主通道报限流时按预设顺序切换。切换之后必须能验证接口是否真的通,而不是切过去继续失败。验证方式很简单:切换后先发一条最小的探测请求,确认返回正常再放量。没有这步验证的自动切换,经常只是把失败从一个通道搬到另一个通道。

降级是另一条路,把请求降到能力弱一点的模型或非流式模式,保住链路不断。这个方法适合后台批处理,不适合前台交互,因为用户对响应质量的变化很敏感。路由规则最好写成配置而不是散在代码里,多模型路由与降级那篇有现成的组织方式,可以直接参考。有一点必须提前确认:有些平台禁止多账号绕额度的做法,动手之前先看服务条款,别把技术方案建在违规操作上。

处置手段适用场景不适用场景
切备用 Key主 Key 额度紧张、备用额度充足账号级配额已满,换 Key 无效
切备用通道单站点通道限流、有其他可用站点所有通道共享同一上游限制
降级模型后台批处理、延迟不敏感前台交互、对质量有硬要求
转非流式只需最终结果、不需要逐字输出需要流式体验的产品

长期规避:把限流当成容量问题来管

限流不是一次性的故障,它是容量规划的一部分。把每个任务设上预算:每分钟请求数、重试次数、总耗时,超了就报警而不是硬扛。报警的意义在于让你知道系统正在接近上限,而不是等它已经挂了才发现。预算值不是拍脑袋定的,用前面量出来的阈值打个余量,留出应对突发流量的空间。

上线前做一轮小评测,看峰值 QPS 落在额度的什么位置。如果峰值已经贴着阈值,那说明没有余量,任何一次流量波动都会变成限流事故。关键业务别只指望一个厂商,把降级路径写进设计文档,出问题的时候按文档执行,而不是临时开会决定。这样做的成本不高,但能显著降低限流带来的业务中断时间。

  • 给每个任务设每分钟请求数上限与重试次数上限
  • 定期看峰值与阈值之间的余量,余量太小就提前扩容或分流
  • 降级路径写进设计文档,出事时按文档走而不是临时决策
  • 用站内价格追踪页面定期核对额度口径变化,避免沿用旧假设

这些做法什么时候不适用

上面这套流程解决的是技术层面的容量问题,但有些限流不是技术问题。如果被限制是因为账号被风控或者违反了服务条款,那任何退避、分流、切片都只是拖延,正确动作是去核对服务条款并联系平台。这种情况下继续折腾技术手段,只会让账号状态更糟。判断标准很简单:看错误信息里有没有条款或风控相关的字样,有就先停下技术动作。

还有一种情况是限流发生在网关超时这类网络层问题上。这时候退避只会让事情更慢,应该去查网关超时与重试的排查顺序,问题根因在链路而不是在调用频率。最后,如果任务本身就是离线批处理、对延迟不敏感,那就直接慢跑,别引入复杂的队列和路由,多一层组件就多一层维护成本。

把阈值和额度口径先对齐站内价格追踪页面有各家额度与计费口径的当前记录,去核对一下你正在用的那条通道,把阈值量准之后再调并发和重试策略,比在代码里反复试参数快得多。

常见问题

加了重试为什么反而更容易被限?

多半是没加抖动,多个请求或进程在同一时间点重试,形成新的尖峰。退避时间要带随机浮动,同时限制同时在飞的请求数,把重试从「一起冲」变成「排队等」。

同一个 Key 在本地不报错,部署到服务器就报,为什么?

常见原因是服务器上的多进程、多容器各跑了一份并发,总量远超本地测试。先统计服务端的实际并发数,再和本地脚本对比,多数情况一看总数就明白了。

换一个中转站能不能绕过限流?

要看限的是哪一层。如果限的是上游账号级配额,换通道只是换了个排队位置;如果限的是通道自身的并发,换一家确实可能变快。切换后按切换中转站后的验证流程测一遍再上量。

限流恢复后能马上按原速率跑吗?

不建议。先降到触发限流时速率的一半左右观察几分钟,稳定后再逐步加压。直接满速恢复很容易二次触发,而且很多平台对短时间内的反复触发会更保守。

怎么判断是我的问题还是平台的问题?

用最小请求(单并发、固定间隔)测同一接口,如果最小请求也被拒,大概率是平台侧;如果最小请求正常,问题出在你这边的并发或调用方式。可以对照服务监控页面看当时是否有抖动。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。