先给结论:限流发生时,最该保护的不是“继续跑完”,而是已经拿到的结果和“哪些还没跑”的边界。可行做法是让脚本在请求前先落盘任务状态、在写入结果时用追加而非覆盖、在遇到限流时把未完成项标记为待重试,而不是整批重来。这样即使被限流中断,你也能从断点继续,而不会丢掉已完成的部分。
假设你有一个批量任务,用脚本循环调用某快照更新软件的接口,对一千个目标逐条触发更新并取回结果。脚本跑到中途,接口开始返回限流类错误。此时有两种常见反应:一是让脚本继续重试,二是直接杀掉进程稍后整批重跑。前者可能让限流升级、把已完成的结果也拖入失败;后者会浪费已经成功的部分。下面用一个明确标注为假设的例子,把决策过程拆开。
在这个假设里,如果没有断点记录,你无法区分“400 条成功”和“400 条里有一部分其实是被限流后误判为失败”。这就是限流场景下最容易被忽略的损失:结果文件被覆盖或状态被重置,已完成的部分反而消失。
看到请求失败率突然升高,不要立刻断定“工具坏了”或“任务必须重来”。用可核对的证据区分几种解释,才能决定下一步动作。
把这三类混在一起,是导致“重跑整批”的常见原因。区分开之后,你只需要对第一类等待重试,对第二类调整节奏,对第三类单独处理。
在每次调用之前,把当前处理到哪一条、该条的状态写成“进行中”并落盘。这样即使进程被杀,重启时也能读到“上一次进行中的是哪条”,而不是从第一条重新开始。这一步的成本很低,却决定了限流后能不能续跑。
把每条成功结果追加到结果文件,而不是等全部跑完再一次性写出。追加写的坏处是文件里可能有重复项,但好处是限流中断时已完成的部分一定还在。重复项可以在最后用唯一标识去重,丢结果却无法找回。
收到限流响应时,先按一个递增的等待时间退避,再重试当前这条。如果连续多次仍被限流,就停止本轮,把剩余项标记为待重试,等下一轮再跑。关键是“停止本轮”不等于“丢弃进度”,前面的状态记录让下一轮能从断点接上。
具体动作:把脚本改成“每条请求前写状态、每条成功后追加结果、限流时退避并记录待重试项”。做完这一步,限流再次发生时,你会得到三份可核对的东西——已完成结果文件、待重试清单、以及最后一次进行中的位置。
这三份东西会直接影响下一步:如果待重试清单很短,说明大部分已完成,只需等一个窗口后重跑剩余项;如果待重试清单几乎等于总量,说明限流可能来自脚本节奏本身,应先降并发或加长间隔,而不是马上重跑。也就是说,保护已有结果不只是“别丢数据”,它还给你提供了判断限流性质的证据。
以上做法成立的前提是:调用方能够区分“成功”“失败”“限流”三种状态,并且接口返回里带有可判断的语义。如果某快照更新软件只返回统一的失败码,你就需要先用小批量测试确认限流时的返回特征,再决定如何分类。具体到某个品牌工具的错误码、配额规则和重试建议,属于会变动的信息,应以该工具当时的官方说明为准,不要照搬本文的假设分类。
最后提醒一个反直觉点:限流期间请求量下降,不能单独证明你的保护策略生效,因为也可能是任务已经跑完或脚本提前退出。要结合待重试清单和进行中位置一起看,才能确认是“被限流但进度保住了”,而不是“什么都没做”。把这三份记录对齐,你才能在限流后做出继续、降速还是排查单条数据的决定。