← 中文资源

简体中文版本

Sleep 节流 API 轮询

以节流轮询等待异步任务完成:基础间隔起步、未完成时间隔指数翻倍封顶、状态分类处理、尝试次数硬上限。

阅读英文原文

示例说明

轮询的礼貌程度由间隔决定:五秒起步的固定间隔在任务要跑十分钟时产生一百多次无效请求。指数增长的间隔——五、十、二十、四十、六十封顶——让请求次数对数下降,API 压力与等待时长的平衡点自动逼近。硬上限是保险丝:任务卡死时脚本在有限时间内放弃并报错,而不是无限等下去。

技术要点

  • 间隔指数增长并封顶防过长等待
  • 状态分类驱动不同处置分支
  • 尝试上限作为整体保险丝

代码解读

wait_for_completion 的每轮先请求再按状态分支:完成输出结果 URL 返回零、失败带错误信息返回一、进行中输出进度并进入退避;网络失败与未完成共用退避路径但消息不同。间隔翻倍逻辑在封顶判断后执行 sleep。MAX_ATTEMPTS 二十次配六十秒封顶,总时长可控。

使用建议

更优雅的方案是服务端 webhook 通知。Retry-After 头出现时应覆盖本地间隔。轮询期间的日志输出便于估算剩余等待。

代码示例

# throttle the polling rate to stay under the API limit
#!/usr/bin/env bash
set -euo pipefail

readonly URL="https://api.example.com/jobs/${JOB_ID:-job-1}"
readonly BASE_INTERVAL=5
readonly MAX_INTERVAL=60
readonly MAX_ATTEMPTS=20

wait_for_completion() {
  local interval=${BASE_INTERVAL}
  local attempt=0

  while (( attempt < MAX_ATTEMPTS )); do
    (( attempt++ )) || true

    local status body
    body=$(curl -sf --max-time 8 "${URL}") || {
      echo "请求失败,${interval}s 后重试" >&2
      sleep "${interval}"
      continue
    }

    status=$(echo "${body}" | jq -r '.status')

    case "${status}" in
      completed)
        echo "${body}" | jq -r '.resultUrl'
        return 0
        ;;
      failed)
        echo "Error: 任务失败:$(echo "${body}" | jq -r '.error')" >&2
        return 1
        ;;
      pending|running)
        echo "状态 ${status},${interval}s 后再查 (${attempt}/${MAX_ATTEMPTS})" >&2
        ;;
      *)
        echo "未知状态: ${status}" >&2
        return 2
        ;;
    esac

    # 未完成时退避增长:对 API 更礼貌
    interval=$(( interval * 2 ))
    (( interval > MAX_INTERVAL )) && interval=${MAX_INTERVAL}
    sleep "${interval}"
  done

  echo "Error: 轮询 ${MAX_ATTEMPTS} 次仍未完成" >&2
  return 1
}

wait_for_completion