问答

批量任务里重试数据接口,怎么保证不被重复扣费?

简短回答

给每个请求一个由接口和参数算出来的重试键,调用超时或连接断开时,用同一个键重发。在 okdata 上,24 小时内的重复请求会返回保存的结果、不扣费,所以一个 1500 页的抖音评论任务不管重试多少次,都是 $3.00。只重试标明可以重试的错误,内容不存在的错误永远不要重试。

一个夜间任务要给 500 条抖音视频各拉 3 页评论。这 1500 次调用里,总会有几次超时或断线。最麻烦的是没收到任何响应的那种:调用可能已经完成并扣了费,也可能没有。盲目重试可能付两次钱,不重试数据就缺一块。幂等键就是用来解决这个两难的。

幂等键怎么起作用

  • 每个请求带一个 Idempotency-Key 请求头。
  • 24 小时内同一个 key、同一个请求,返回保存的结果:meta.replayed: true,扣费 "0",响应头带 Idempotent-Replayed: true。
  • 如果第一次失败了,什么都不会保存,重试就是重新跑一次;失败的那次本来就没扣费。
  • 如果第一次还在跑,会返回 rate_limited,retry_after_seconds: 1,等一下再发。
  • 同一个 key 用在不同的请求上,会被拒绝并返回 invalid_params,所以要按请求生成 key,整个任务共用一个是行不通的。

幂等键能保留多久,是每个用这套机制的 API 都会被问到的问题,比如这个关于支付接口幂等键的提问。这里的答案是 24 小时。

哪些情况该重试

返回码 要不要重试
rate_limited 要,等 retry_after_seconds
upstream_busy、upstream_failed、internal_error 要,退避后重试
timeout,或者完全没收到响应 要,用同一个 key
not_found、invalid_params、unauthorized、insufficient_balance 不要,修正或跳过

可以直接粘贴的重试函数

import hashlib, json, os, time, requests

KEY = os.environ["OKDATA_API_KEY"]

def call(path, **params):
    idem = hashlib.sha256(json.dumps([path, params], sort_keys=True).encode()).hexdigest()
    h = {"Authorization": f"Bearer {KEY}", "Idempotency-Key": idem}
    for attempt in range(6):
        try:
            body = requests.get(f"https://api.okdata.online{path}", headers=h, params=params, timeout=90).json()
        except requests.RequestException:
            time.sleep(2 ** attempt)              # 没收到响应:用同一个 key 重发
            continue
        if body["code"] == "ok":
            return body
        if body.get("retry") not in ("yes", "same_key"):
            raise RuntimeError(f"{body['code']}: {body['message']}")
        time.sleep(body.get("retry_after_seconds") or 2 ** attempt)
    raise RuntimeError(f"重试多次仍失败: {path} {params}")

page = call("/v1/douyin/videos/comments/list", id="7301234567890123456", sort="hot")

同一个函数也适用于 TikTok 视频评论和其他所有接口;抖音视频评论每页 $0.002,重放不收费。

再养成两个习惯

  • 保存每次返回的 request_id。用 GET https://api.okdata.online/v1/calls/{request_id} 能查到任何一次调用最后怎么结算。
  • 给重试次数封顶。重试六次还失败,通常是出了故障;看一眼服务状态,晚些时候用同样的 key 重跑整个任务。

全部返回码、哪些扣费、哪些值得重试,见错误码。

更新于