限流发生时,最该保护的不是"还能不能继续查",而是已经拿到的结果是否完整、可复用、可续跑。如果脚本把结果只存在内存里,一次429或超时就可能让前几十分钟的查询归零;如果每拿到一条就落盘并记录游标,限流只影响进度,不影响已有产出。
同样是返回429或连接被拒,背后的原因不同,处理策略也不同。
区分方法:把失败时间戳和成功时间戳按分钟聚合,看被拒是否集中在固定位置。如果每次都在第N次附近,偏配额型;如果分布随机且与其它任务时段重合,偏突发型或共享额度型。这个判断直接决定下一步——配额型要改速率,突发型要先隔离调用来源。
保护已有结果的核心动作是:每条结果写入后立即持久化,并同步记录已完成的位置标识。位置标识可以是查询列表的索引、上一批的结束ID或时间戳,只要下次启动时能据此跳过已完成的条目。
假设一个场景:脚本要查500个条目的权重指标,每20条写一次文件。查到第180条时被限流中断,最后一批只写入了12条。重启后如果从第180条继续,那8条未写入的就会永久缺失;如果从第160条重跑,则产生重复。两种都不理想。改成每条写入、游标随写随更新后,重启从第181条开始,既不缺也不重。
这里的关键取舍是写入频率与性能。逐条写盘会增加I/O开销,但对权重查询这类本身受网络延迟主导的任务,这点开销通常可以接受。若确实需要批量写,至少保证游标更新与数据写入在同一事务或同一原子操作内完成,避免"数据写了游标没更新"或反之。
这个顺序的意义在于:先保住数据,再诊断原因,最后才恢复调用。反过来做——先重试、再想数据丢没丢——往往两头都保不住。
限流期间拿到的结果未必都有效。需要警惕两类情况:一是限流前最后几条返回了空值或默认值,被当成正常结果写入;二是部分接口在限流时返回200但内容为错误提示。判断依据是响应体的结构是否与正常结果一致,而不是只看状态码。
实际操作中,可以在写入前加一个校验:字段是否齐全、数值是否落在合理区间、是否与上一条完全相同。校验不通过的不写游标,留待续跑时重查。这样即使限流期间混入了异常响应,也不会污染已有结果集。
需要说明的是,请求量下降或某段时间抓取归零,本身不能证明限流处理正确——也可能是任务已经跑完、上游数据源临时不可用或脚本提前退出。要结合游标位置和日志时间线一起看,才能判断是"被限流"还是"已完成"。
与其等限流发生后再补救,不如在脚本里预设检查点:每完成一个批次记录时间、成功数、失败数;捕获限流异常时先落盘再退出,而不是直接抛出中断。这样无论任务是被手动停止还是被限流打断,下次启动都能从最近的检查点继续。具体工具的重试参数、配额规则和返回格式需要以该工具的当前文档为准,不同实现的默认行为差异较大。
保护已有结果的本质,是让"进度"成为一个独立于进程生命周期的持久状态。做到这一点,限流就只是变慢,而不是重来。