结论先行:如果限流发生在分页或分批抓取的中途,保护已有结果的关键不是“继续重试到成功”,而是把已返回的数据落盘成可校验的分片,并让重试从最后一个完整分片之后继续。只有当你能确定被截断的那一批数据没有被写入、且下一批的起始游标仍然有效时,继续重试才是安全的;否则重试会把缺口掩盖成“看起来完整”的结果。
脚本调用外链检查工具时,限流可能发生在三个不同层面,对应三种不同的保护动作。
判断属于哪一层,靠的是响应元数据而不是感觉:HTTP 状态码、响应体的长度是否与声明的长度一致、分页字段是否仍然返回同一个起始位置。把这些字段记进日志,比事后猜原因可靠得多。
很多人写脚本时用追加模式,一条一条往同一个文件里写。限流中断后,这个文件既不知道写到哪,也无法判断中间有没有缺行。改成按批落盘的分片结构,情况会好很多。
假设每批抓取 100 条外链记录,那么每批写成一个独立文件,文件名带上批次序号和该批的起始游标,例如 batch_0007_start_abc123.json。文件内部记录该批的条数、首尾记录标识和写入时间。这样做的直接结果是:中断后你只需要检查最后几个分片,就能确定最后一个完整批次在哪里,重试起点也就有了明确依据。
配合一个清单文件记录“哪些批次已完成、哪些批次待重试”,可以把恢复逻辑从“猜”变成“查”。清单本身要最后写入,避免出现分片已写但清单未更新的中间状态。
限流恢复后不要立刻从头跑。先做三步核对,再决定下一步动作。
这一步的产出是一份“可信批次列表”。后续只重试不在列表里的批次,而不是重跑全部。这样既省调用次数,也避免重复数据污染最终结果。
前面说的方法有一个明确的反例:当工具的分页基于一个会随会话变化的游标,而限流又恰好重置了会话时,旧游标可能指向一个语义已经不同的位置。此时按旧游标继续重试,拿回来的数据可能既不重复也不缺失,而是错位——它属于另一个时间点的结果集。
识别这种反例的证据是:重试返回的第一条记录标识,与中断前预期的下一条不一致,但接口没有报错。一旦出现这种情况,正确的动作是放弃当前游标,从该批次之前的最后一个可信锚点重新开始,并把这次事件记录为“游标语义变更”,而不是当成普通限流。
如果无法确定游标是否会随会话变化,保守做法是把每个批次的起始游标也写进分片,恢复时优先使用分片内记录的游标,而不是脚本内存里的游标。
综合下来,限流后的恢复顺序可以固定为:读取清单,确定最后一个完整批次;对后续分片做缺口核对,标记可疑分片;用分片内记录的游标而非内存游标发起重试;重试成功后先更新清单,再继续下一批。每一步的结果都会影响下一步:如果缺口核对发现游标语义变更,就不能按普通重试处理,而要退回锚点;如果重试再次被限流,则延长退避并保持分片不变,等待下一次核对。
这套顺序的价值在于,它把“限流”从一个需要临场判断的异常,变成一个有固定分支的流程。只要分片和清单是可信的,限流本身不会损坏已经拿到的结果,真正需要防的是重试时把缺口写成了完整。