Comprueba los reintentos y los tiempos de espera en aplicaciones .NET que utilizan Microsoft.Extensions.Http.Resilience

Tip

¿Eres nuevo en la limitación de tráfico? Aprenda qué es la limitación de velocidad y cómo gestionarla.

De un vistazo
Objetivo: Confirmar que tu controlador de resiliencia HTTP de .NET realiza reintentos, aplica retrasos y agota el tiempo de espera según lo previsto
Tiempo: 15 minutos
Plugins:GenericRandomErrorPlugin, RetryAfterPlugin, LatencyPlugin
Requisitos previos:Configurar Dev Proxy, una aplicación de .NET que usa Microsoft.Extensions.Http.Resilience

Ha agregado AddStandardResilienceHandler() a su HttpClient. ¿Cómo sabes que funciona? Las API a las que accedes rara vez fallan de forma intencionada, y las pruebas unitarias que simulan HttpMessageHandler omiten el proceso de resiliencia que deseas probar.

Dev Proxy se sitúa entre tu aplicación y la API. Devuelve errores y respuestas lentas a la aplicación, por lo que la aplicación se ejecuta sin cambios y el controlador de resiliencia reacciona a respuestas HTTP reales. Verá todos los intentos en la salida del proxy de desarrollo.

Qué hace el controlador de resiliencia estándar

Antes de probarlo, sepa qué esperar. Con las opciones predeterminadas, AddStandardResilienceHandler():

Behavior Predeterminado
Reintentos: HTTP 500 y superiores, 408, 429, HttpRequestException y TimeoutRejectedException
Número de reintentos 3, con retraso exponencial y fluctuación, a partir de 2 segundos
Retry-After encabezado Cumplido. El controlador espera el tiempo que solicita la API.
Tiempo de espera de intento 10 segundos por intento
Tiempo de expiración total 30 segundos para la solicitud, incluidos todos los reintentos

Para obtener la lista completa de estrategias y sus valores predeterminados, consulte Valores predeterminados del controlador de resiliencia estándar.

Enruta tu aplicación a través de Dev Proxy

.NET usa el proxy del sistema, por lo que, al iniciar Dev Proxy, este intercepta las solicitudes de la aplicación sin cambios en el código. Para obtener más información, consulte Usar Dev Proxy con aplicaciones .NET.

Simular errores transitorios

Crea una configuración de Dev Proxy que falle las solicitudes a tu API con los errores que el controlador vuelve a intentar. En este ejemplo se usa https://api.contoso.com. Reemplácelo por la dirección URL de la API a la que llama la aplicación.

Archivo: 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

Añada el RetryAfterPlugin antes del GenericRandomErrorPlugin en su archivo de configuración. Si lo agrega después, el GenericRandomErrorPlugin rechaza la solicitud antes de que el RetryAfterPlugin pueda comprobarla.

En el archivo de errores, defina una respuesta de limitación y dos errores de servidor. El valor @dynamic establece el encabezado Retry-After e indica a RetryAfterPlugin que compruebe que tu aplicación espera ese tiempo antes de volver a llamar a la API.

Archivo: 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
        }
      ]
    }
  ]
}

Inicie el proxy de desarrollo y ejecute la aplicación.

devproxy --config-file devproxyrc.json

Con una tasa de fallos del 50 %, la mayoría de las solicitudes se recuperan después de uno o dos reintentos. En la salida del proxy de desarrollo, compruebe que:

  • Tras una respuesta 429, el siguiente intento a la misma URL se produce tras el tiempo Retry-After. Dev Proxy usa 5 segundos de forma predeterminada. Si tu aplicación llama a la API demasiado pronto, el RetryAfterPlugin lo notifica y limita la solicitud.
  • La aplicación no envía más intentos de los configurados.
  • Las solicitudes que no desea reintentar, como una POST que crea un registro, solo se envían una vez. Para excluirlos, llame a options.Retry.DisableForUnsafeHttpMethods() o options.Retry.DisableFor(...).

Prueba qué sucede cuando se agotan los reintentos

Los reintentos ocultan fallos breves. También debe saber qué hace la aplicación cuando la API sigue fallando. Inicia Dev Proxy con una tasa de fallo del 100 %:

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

Dev Proxy muestra 4 intentos para cada solicitud: la solicitud original y 3 reintentos. Después del último reintento, el controlador estándar no lanza una excepción. Devuelve la última respuesta de error a tu código. Compruebe qué hace su aplicación con ello. Por ejemplo, EnsureSuccessStatusCode() lanza un HttpRequestException, y GetStringAsync() también lanza un error. Asegúrese de que la aplicación muestre un mensaje útil o recurra a una alternativa, en lugar de que se bloquee o muestre un error genérico.

Probar los tiempos de espera

Las API lentas desencadenan una ruta de acceso diferente en el controlador. Para probarlo, use LatencyPlugin para retrasar las respuestas más allá del tiempo de espera de intento de 10 segundos.

Archivo: 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
  }
}

Inicie el proxy de desarrollo y ejecute la aplicación. Cada intento tarda más de 10 segundos, por lo que el tiempo de espera del intento lo cancela y el controlador vuelve a intentarlo. Tras 30 segundos, el tiempo de espera total cancela la solicitud y tu código recibe un TimeoutRejectedException. Compruebe que la aplicación lo captura y le indica al usuario lo que ha ocurrido.

Tip

Para probar los mismos escenarios con tu propia configuración de resiliencia, cambia los valores en tu llamada AddStandardResilienceHandler(options => ...) y vuelve a ejecutar las mismas configuraciones de Dev Proxy.

Paso siguiente

Obtenga más información sobre cómo simular la limitación en cualquier API.

Consulte también