限流发生时,最该保护的不是“继续拉取”的能力,而是已经拿到的结果和可复现的进度。做法是把每次调用结果先落到本地或自有存储,再根据返回状态区分“暂时失败”和“永久失败”,最后只重试前者。这样即使脚本被限流,已有数据不会丢,下一次运行也能从断点继续。
一个常见矛盾是:脚本被限流后,很多人第一反应是清空重跑,觉得“重来一次更干净”。但限流往往不是单次请求失败,而是整段时间内配额被压缩。此时重跑会再次触发限制,还可能覆盖掉之前已经成功的结果。
另一种解释是限流只是表象,真正原因是脚本没有区分“可重试”和“不可重试”的响应。如果所有失败都当成同一类处理,就会把参数错误、权限不足也塞进重试队列,导致无效请求反复占用额度。
要判断属于哪一种,可以看三个信号:
能区分两者的关键证据是:把失败响应按错误类型分组,再看每组是否随时间恢复。限流类失败通常会在等待后恢复;参数或权限类失败不会因为等待而改变。
具体动作是:每次调用返回后,无论成功还是失败,先把原始响应和请求标识写入一个追加式文件,例如按日期分文件,每条记录包含时间、请求参数摘要、返回状态和原始内容。不要等整批跑完再统一保存。
这个动作的结果会直接影响下一步:如果限流突然发生,你至少能知道哪些请求已经成功、哪些还没发出。下一次运行时,先读取这个文件,跳过已有成功记录,只补发缺失部分。这样就把“保护已有结果”变成了“基于已有结果做增量补齐”。
假设一个脚本需要处理 100 个对象,跑到第 40 个时被限流。如果每次结果都追加落盘,恢复后只需从第 41 个继续;如果只在内存里保存,进程退出后前 40 个结果也丢了,只能全部重来。这个对比不说明哪种工具更好,只说明落盘时机决定了限流后的恢复成本。
重试阶段最容易犯的错是用新结果覆盖旧文件。更稳妥的做法是保留原始文件,另建一个状态文件记录每个对象的处理状态,例如 pending、done、failed_retryable、failed_final。每次重试只更新状态,不删除历史记录。
这样做的实际影响是:当限流反复出现时,你能清楚看到哪些对象从未成功、哪些成功过但后来失败。后续决策——是继续等待、缩小批量还是人工处理——都基于这些状态,而不是凭感觉重跑。
不同工具的限流表现和返回格式并不相同,具体阈值、错误码和恢复时间需要以实际调用返回和官方说明为准。脚本里应避免写死“等几秒就一定恢复”这类假设,而是把等待时间、重试次数和批量大小做成可调整参数。限流期间,优先保证已有结果可读、可续,而不是追求单次跑完。