概览
目标:测试应用如何处理GitHub REST API 速率限制
时间: 15 分钟
插件:RateLimitingPlugin、GenericRandomErrorPlugin、RetryAfterPlugin
先决条件:设置开发代理
你的应用调用 GitHub API。 它在你的机器上可以正常工作,然后一个 CI 作业、一个大型组织或繁忙的一天使它超过速率限制,它开始失败。 若要针对真实 API 测试它,你需要用尽速率限制允许的请求额度,然后最多等待一小时才能再次尝试。 Dev Proxy 在本地模拟 GitHub 速率限制,并具有你选择的限制和时间范围。
了解GitHub返回的内容
GitHub REST API 有 2 种速率限制。
主要速率限制 限制每小时发出的请求数。 例如,对于未经身份验证的请求为 60,对于具有个人访问令牌的请求为 5,000。 每个响应都包含显示你当前状况的标头:
| Header | Meaning |
|---|---|
x-ratelimit-limit |
每小时的最大请求数 |
x-ratelimit-remaining |
当前窗口中剩余的请求数 |
x-ratelimit-reset |
窗口重置的时间(以 UTC 纪元秒为单位) |
当你超过主限制时,GitHub 返回403或429,并将x-ratelimit-remaining设置为0。 在 x-ratelimit-reset 中显示的时间之前,请勿重试。
辅助速率限制保护 GitHub 免受突发流量影响,例如过多的并发请求或过快创建过多内容。 当你超过其中一个限制时,GitHub 返回403或429,并附带一条关于辅助速率限制的消息。 如果响应有retry-after标头,请等待相应的秒数。 否则,请至少等待一分钟,如果请求持续失败,请增加等待时间。
GitHub可以封禁在速率受限的情况下继续发送请求的集成。 有关详细信息,请参阅GitHub文档中 REST API 的速率限制。
模拟主速率限制
使用 RateLimitingPlugin 对请求进行计数,并返回GitHub的速率限制标头。 若要在不等待一小时的情况下进行测试,请使用小限制和短窗口。
文件: devproxyrc.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
"plugins": [
{
"name": "RetryAfterPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
},
{
"name": "RateLimitingPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "githubRateLimit"
}
],
"urlsToWatch": [
"https://api.github.com/*"
],
"githubRateLimit": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/ratelimitingplugin.schema.json",
"headerLimit": "x-ratelimit-limit",
"headerRemaining": "x-ratelimit-remaining",
"headerReset": "x-ratelimit-reset",
"resetFormat": "UtcEpochSeconds",
"costPerRequest": 1,
"rateLimit": 5,
"resetTimeWindowSeconds": 60,
"warningThresholdPercent": 0,
"whenLimitExceeded": "Custom",
"customResponseFile": "github-rate-limit-exceeded.json"
}
}
Caution
在您的配置文件中,于 RetryAfterPlugin 之前添加 RateLimitingPlugin。 如果你在之后添加它,RateLimitingPlugin会在RetryAfterPlugin能够检查请求之前处理该请求。
在自定义响应文件中,定义GitHub超出主要速率限制时返回的响应。
文件: github-rate-limit-exceeded.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/ratelimitingplugin.customresponsefile.schema.json",
"statusCode": 429,
"headers": [
{
"name": "content-type",
"value": "application/json; charset=utf-8"
}
],
"body": {
"message": "API rate limit exceeded for user ID 1.",
"documentation_url": "https://docs.github.com/rest/overview/rate-limits-for-the-rest-api"
}
}
启动开发代理并运行应用。
devproxy --config-file devproxyrc.json
开发代理将每分钟的前 5 个请求转发到GitHub,并在响应中设置x-ratelimit-*标头。 从第 6 个请求开始,Dev Proxy 返回限流响应,将x-ratelimit-remaining设置为0,并将x-ratelimit-reset设置为窗口结束时间。 如果应用在窗口重置之前再次调用 API,则 RetryAfterPlugin 会报告此情况并对该请求进行限流。
检查你的应用是否:
- 读取
x-ratelimit-remaining,并在到达0之前减速。 - 停止在速率限制响应后调用 API,并等到
x-ratelimit-reset。 - 告知用户发生的情况,例如“达到GitHub速率限制,以 14:05 重试”,而不是以无提示方式失败。
Note
当你超出速率限制时,GitHub 返回 403 或 429。 若要测试你的应用是否也能处理403,请将403更改为statusCode。
RetryAfterPlugin仅跟踪429响应,因此不会报告收到403后发生的过早重试。
Tip
Dev Proxy 将请求转发到 GitHub,直到达到模拟限制。 这些请求也会计入你的真实 GitHub 速率限制。
模拟次要速率限制
辅助速率限制以突发方式出现,并包含retry-after标头。 使用 GenericRandomErrorPlugin 以随机返回它们。
文件: devproxyrc.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
"plugins": [
{
"name": "RetryAfterPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
},
{
"name": "GenericRandomErrorPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "githubSecondaryRateLimit"
}
],
"urlsToWatch": [
"https://api.github.com/*"
],
"githubSecondaryRateLimit": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.schema.json",
"errorsFile": "github-secondary-rate-limit.json",
"rate": 50,
"retryAfterInSeconds": 60
}
}
文件: github-secondary-rate-limit.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
"errors": [
{
"request": {
"url": "https://api.github.com/*"
},
"responses": [
{
"statusCode": 429,
"headers": [
{
"name": "content-type",
"value": "application/json; charset=utf-8"
},
{
"name": "retry-after",
"value": "@dynamic"
}
],
"body": {
"message": "You have exceeded a secondary rate limit. Please wait a few minutes before you try again.",
"documentation_url": "https://docs.github.com/rest/overview/rate-limits-for-the-rest-api#about-secondary-rate-limits"
}
}
]
}
]
}
启动开发代理并运行应用。 在应用再次调用 API 之前,请检查应用是否等待了 retry-after 标头中指定的秒数。 如果它没有这样做,RetryAfterPlugin会报告此情况。
如果将 Octokit 与节流插件配合使用,请检查你的 onRateLimit 和 onSecondaryRateLimit 处理程序是否运行,以及它们是否返回预期的结果。
后续步骤
了解更多关于 RateLimitingPlugin 的信息。
另请参阅
- 模拟 Rate-Limit API 响应 - 任何 API 的速率限制
- 测试我的应用程序是否正确处理节流 - 任何 API 上的节流
- RetryAfterPlugin - 验证重试行为
- 在 CI/CD 中使用开发代理 - 在管道中自动执行复原测试