GitHub API 速率限制已超出:含义以及如何处理它

GitHub限制应用可以发送的 REST API 请求数。 超过限制时,GitHub 会拒绝你的请求,并返回403或429状态,直到限制重置或等待时间过去。 GitHub有 2 种限制:每小时请求的主要限制,以及防止突发的辅助限制。 如果你的应用在受到速率限制时仍不断发送请求,GitHub 可能会禁止你的集成。 有关详细信息,请参阅 REST API 的速率限制。

GitHub 速率限制概览

Limit 价值 超出限制时的情况
主速率限制,未经身份验证 每个 IP 地址每小时 60 个请求 403或429,并且x-ratelimit-remaining为0
主速率限制,个人访问令牌 每小时 5,000 个请求 同上
主速率限制,GitHub Actions 中的GITHUB_TOKEN 每个存储库每小时 1,000 个请求 同上
次要 例如,并发请求不超过 100 个,REST 终结点每分钟 900 点,每分钟约 80 个内容生成请求 403 或 429 带有错误消息。 retry-after 可能存在。

GitHub 可以在不另行通知的情况下更改辅助限制,并且也无法查看自己离这些限制还有多近。

每个响应都包含一个标头,用于告知你当前在主要限制下的状态:

Header 它告诉你什么
x-ratelimit-limit 你每小时可发送的最多请求
x-ratelimit-remaining 当前窗口中剩余的请求数
x-ratelimit-used 您在当前时间窗口内发送了多少个请求
x-ratelimit-reset 当窗口重置时,以 UTC 纪元秒为单位
x-ratelimit-resource 请求计入哪个限制

如何处理GitHub速率限制

  1. 区分速率限制和权限错误。 当令牌缺少访问权限时,GitHub也会返回403。 如果响应没有 retry-after,x-ratelimit-remaining 不是 0,并且消息未提及速率限制,则这是权限问题。 不要重试该请求。
  2. 首先遵循retry-after。 如果该标头存在,请等待其指定的秒数。
  3. 否则,请等待重置。 如果0为x-ratelimit-remaining,请在x-ratelimit-reset中的时间之前不要重试。
  4. 否则,请等待至少 1 分钟。 对于没有任一标头的辅助限制,GitHub要求你等待至少 1 分钟,并在每次重试失败后等待更长时间。 在达到设定的重试次数后停止并报错。
  5. 在请求配额用尽之前降低请求速率。使用x-ratelimit-remaining和x-ratelimit-reset来控制请求节奏。 不要基于精确的剩余数量来编写逻辑,因为 GitHub 可能会更改限制。 标头 x-ratelimit-* 是事实来源,而不是 GET /rate_limit 终结点。
async function githubWaitMs(response, attempt) {
  if (response.status !== 403 && response.status !== 429) {
    return null;
  }
  const retryAfter = response.headers.get('retry-after');
  if (retryAfter) {
    return Number(retryAfter) * 1000;
  }
  if (response.headers.get('x-ratelimit-remaining') === '0') {
    const resetMs = Number(response.headers.get('x-ratelimit-reset')) * 1000;
    return Math.max(resetMs - Date.now(), 0);
  }
  const { message = '' } = await response.clone().json().catch(() => ({}));
  if (response.status === 429 || /rate limit/i.test(message)) {
    return 60_000 * 2 ** attempt;
  }
  // A 403 without rate limit signals is a permission problem: don't retry
  return null;
}

调用方在函数返回数字时重试,并在几次尝试后停止。

如何测试应用处理GitHub速率限制

开发时很少达到GitHub速率限制。 你发送一些请求,每小时 5,000 个请求感觉无休止。 因此,测试速率限制处理的方式决定了你是否在用户之前发现了 bug。

Approach 你找到的内容 您错过的内容
等到投入生产后 真实故障 所有内容,直到用户遇到故障
在测试中模拟 API,或让编码代理编写模拟代码 重试分支是否运行 GitHub的实际标头和错误正文,以及 SDK 的重试策略。 你的应用还需要一个仅限测试的开关才能访问 mock。
调用真正的 API,直到触发限流 真实行为 : 最多需要发送 5,000 个请求,你无法按需触发次要速率限制,并且可能导致你的集成被封禁
拦截应用的实际流量并按需返回速率限制响应 实际 URL、实际 SDK 和重试策略,以及GitHub自己的标头和错误格式 你的应用中没有任何更改,因此它不会对你的代码进行隔离测试。 这件事留给单元测试来处理。

在应用中试用

Dev Proxy拦截你的应用发往api.github.com的请求并返回 GitHub 风格的速率限制响应,同时你的应用继续调用真实 URL。 github-rate-limiting预设按每小时 60 个请求的限制计算你的请求数,发送x-ratelimit-*标头,并在请求配额用尽时返回带API rate limit exceeded的429。在此之前,你的请求会发送到 GitHub,也会计入你的实际限额。

下载预设,并使用它启动 Dev Proxy:

devproxy config get github-rate-limiting
devproxy --config-file "~dataFolder/configs/github-rate-limiting/.devproxy/devproxyrc.json"

若要测试次要速率限制,请改为从同一文件夹使用devproxyrc-secondary.json启动 Dev Proxy。 它会随机返回带retry-after标头的次要速率限制429。

然后像往常一样运行应用并观察其运行情况。 若要安装 Dev Proxy,请参阅 “设置 Dev Proxy”。

后续步骤

另请参阅