限流发生时,最该保护的不是“继续跑完”,而是已经拿到的结果和已经消耗掉的调用预算。一个常见矛盾是:脚本被限流后,你看到部分数据返回、部分报错,直觉会认为“再重试几次就补齐了”,但真正决定下一步的,是判断这次限流属于请求节奏过快还是配额已耗尽。前者可以通过退避重试恢复,后者重试只会扩大报错面,还可能让已完成的结果被覆盖或写坏。
节奏型限流通常表现为短时间窗口内连续返回限流错误,隔一段时间后同样请求能成功。配额型限流则相反,无论怎么等待,当天或当前周期内都不再放行,错误信息可能更稳定、更一致。两种解释对应的动作完全不同:节奏型适合指数退避加队列串行;配额型适合立即停止、落盘、切换数据来源或延后到下一周期。
能区分两者的证据有三类。第一,看错误返回是否带有明确的配额字段或周期提示,有周期提示基本可归为配额型。第二,做一次最小探测请求:暂停一段固定时间后只发一个请求,如果成功,偏节奏型;如果仍失败,偏配额型。第三,看失败是否集中在某一类接口,比如批量查询接口全挂、单条接口仍可用,那更可能是接口级限制而非整体封禁。
保护已有结果的实际动作是:在脚本里把每次成功返回立即追加写入一个中间文件,而不是等全部跑完再统一写库。中间文件建议按“请求标识+时间戳”命名,并保留原始响应,不要只留解析后的字段。这样即使进程被杀,已成功的结果仍然可用。
这个动作会直接影响下一步:如果中间文件里成功记录已经覆盖了大部分目标,你只需要补跑失败的那一小段,重试成本很低;如果成功记录很少,说明限流发生得很早,继续重试的收益不大,应该先改成更保守的节奏或换时间窗口。判断依据是成功记录占目标的实际比例,而不是脚本是否报错。
很多脚本在限流后仍然保持原来的并发数,只是简单重试,这会持续触发限流。更稳妥的做法是:把并发降为串行或小并发,每次失败后等待时间翻倍,并设置最大重试次数。假设原来并发是10、每次间隔0.1秒,限流后可以改为并发1、间隔从1秒起逐步加倍,并在连续失败到上限时停止而不是无限循环。
需要注意,退避只能解决节奏型限流。如果探测请求证明是配额型,退避没有意义,此时应保存进度、记录已完成的偏移位置,等下一个周期再续跑。续跑时要能识别哪些请求已经成功,避免重复调用浪费本就有限的配额。
请求量下降、报错暂时消失、脚本重新跑通,都不能单独证明限流问题已经处理正确。请求量下降可能只是你减少了任务量;报错消失可能是错误被吞掉或写进了别的日志;脚本跑通可能只是恰好落在限流窗口之外。要确认处理有效,需要同时看到:成功记录在稳定增长、失败率随退避策略下降、并且重复运行不会覆盖已有成功结果。
如果这些条件不满足,下一步不应扩大任务规模,而应回到探测请求,重新确认限流类型,再决定是继续退避还是等待周期恢复。保护已有结果的核心,是让每一次成功都可追溯、可续跑,而不是追求一次跑完。