在使用 Microsoft.Extensions.Http.Resilience 的 .NET 应用中测试重试和超时

Tip

刚接触限流? 了解什么是节流以及如何处理节流问题。

概览
目标:确认 .NET HTTP 弹性处理程序按预期进行重试、退让并超时
时间: 15 分钟
插件:GenericRandomErrorPlugin、RetryAfterPlugin、LatencyPlugin
先决条件:设置 Dev Proxy,这是一个使用 Microsoft.Extensions.Http.Resilience 的 .NET 应用

您已将 AddStandardResilienceHandler() 添加到您的 HttpClient。 你怎么知道它能正常工作? 你调用的 API 很少会在你需要时失败,并且模拟 HttpMessageHandler 的单元测试会跳过你想要测试的复原管道。

开发代理位于应用和 API 之间。 它向应用返回错误和缓慢的响应,因此应用无需更改即可运行,复原处理程序会对真实的 HTTP 响应作出反应。 可在 Dev Proxy 输出中看到每次尝试。

标准弹性处理程序的作用

在测试之前,请了解预期内容。 使用默认选项, AddStandardResilienceHandler():

Behavior 默认
开启重试 HTTP 500 及以上、408、429、HttpRequestException 和 TimeoutRejectedException
重试次数 3,带指数退避和抖动,从 2 秒开始
Retry-After 标头 荣幸。 处理程序等待 API 要求的时长。
尝试超时 每次尝试 10 秒
总超时 请求耗时 30 秒,包括所有重试

有关策略及其默认值的完整列表,请参阅 标准复原处理程序默认值。

通过 Dev Proxy 路由应用

.NET使用系统代理,因此,启动 Dev Proxy 时,它会截获应用的请求,而无需更改代码。 有关详细信息,请参阅将 Dev Proxy 用于 .NET 应用程序。

模拟瞬时错误

创建一个开发代理配置,使对 API 的请求失败,并返回处理程序会重试的错误。 此示例使用 https://api.contoso.com。 将其替换为应用调用的 API 的 URL。

文件: 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": "transientErrors"
    }
  ],
  "urlsToWatch": [
    "https://api.contoso.com/*"
  ],
  "transientErrors": {
    "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.schema.json",
    "errorsFile": "transient-errors.json",
    "rate": 50
  }
}

Caution

在您的配置文件中,于 RetryAfterPlugin 之前添加 GenericRandomErrorPlugin。 如果在之后添加,GenericRandomErrorPlugin 会在 RetryAfterPlugin 检查它之前使请求失败。

在错误文件中,定义限流响应和两个服务器错误。 @dynamic 值设置 Retry-After 标头,并告诉 RetryAfterPlugin 检查应用是否在再次调用 API 之前等待了这么长时间。

文件: transient-errors.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
  "errors": [
    {
      "request": {
        "url": "https://api.contoso.com/*"
      },
      "responses": [
        {
          "statusCode": 429,
          "headers": [
            {
              "name": "Retry-After",
              "value": "@dynamic"
            }
          ]
        },
        {
          "statusCode": 500
        },
        {
          "statusCode": 503
        }
      ]
    }
  ]
}

启动开发代理并运行应用。

devproxy --config-file devproxyrc.json

由于失败率为 50%,大多数请求在一两次重试后成功。 在开发代理输出中,检查是否:

  • 在收到 429 响应后,对同一 URL 的下一次尝试会在 Retry-After 时间之后进行。 默认情况下,Dev Proxy 使用 5 秒。 如果应用调用 API 太早,则 RetryAfterPlugin 会报告这一情况并限制请求。
  • 你的应用发送的尝试次数不会超过你配置的次数。
  • 你不想重试的请求(如 POST 创建记录的请求)只发送一次。 若要排除它们,请使用 options.Retry.DisableForUnsafeHttpMethods() 或 options.Retry.DisableFor(...)。

测试重试用完时会发生什么情况

重试会掩盖短暂故障。 此外,还需要了解 API 持续失败时应用执行的操作。 使用 100% 失败率启动开发代理:

devproxy --config-file devproxyrc.json --failure-rate 100

开发代理显示每个请求的 4 次尝试:原始请求和 3 次重试。 在最后一次重试后,标准处理程序不会引发异常。 它将向您的代码返回最后一个错误响应。 检查你的应用如何处理它。 例如,EnsureSuccessStatusCode()引发HttpRequestException,而GetStringAsync()也会引发。 确保应用显示有用的信息或提供后备方案,而不是崩溃或显示通用错误。

测试超时

慢速 API 在处理器中触发其他路径。 若要对其进行测试,请使用 LatencyPlugin 将响应延迟到超过 10 秒的尝试超时时间。

文件: devproxyrc.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
  "plugins": [
    {
      "name": "LatencyPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "slowApi"
    }
  ],
  "urlsToWatch": [
    "https://api.contoso.com/*"
  ],
  "slowApi": {
    "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/latencyplugin.schema.json",
    "minMs": 11000,
    "maxMs": 15000
  }
}

启动开发代理并运行应用。 每次尝试花费的时间超过 10 秒,因此尝试超时会取消它,处理程序会重试。 30 秒后,总超时将取消请求,你的代码会收到一个 TimeoutRejectedException。 检查你的应用是否捕获它,并告知用户发生了什么情况。

Tip

若要使用自己的复原设置测试相同的方案,请更改你的 AddStandardResilienceHandler(options => ...) 调用中的值,然后再次运行相同的开发代理配置。

后续步骤

详细了解如何模拟任何 API 的限制。

另请参阅