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

Tip

不熟悉限流? 了解什么是节流以及如何处理节流问题。

概览
目标:测试应用如何处理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 的信息。

另请参阅