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速率限制
- 区分速率限制和权限错误。 当令牌缺少访问权限时,GitHub也会返回
403。 如果响应没有retry-after,x-ratelimit-remaining不是0,并且消息未提及速率限制,则这是权限问题。 不要重试该请求。 - 首先遵循
retry-after。 如果该标头存在,请等待其指定的秒数。 - 否则,请等待重置。 如果
0为x-ratelimit-remaining,请在x-ratelimit-reset中的时间之前不要重试。 - 否则,请等待至少 1 分钟。 对于没有任一标头的辅助限制,GitHub要求你等待至少 1 分钟,并在每次重试失败后等待更长时间。 在达到设定的重试次数后停止并报错。
-
在请求配额用尽之前降低请求速率。使用
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”。