Testowanie ponawiania prób i limitów czasu w aplikacjach .NET korzystających z Microsoft.Extensions.Http.Resilience

Wskazówka

Nie znasz ograniczania przepustowości? Dowiedz się , czym jest ograniczanie przepustowości i jak go obsłużyć.

Na pierwszy rzut oka
Cel: Upewnij się, że moduł obsługi odporności HTTP platformy .NET ponawia próby, stosuje opóźnienia między ponowieniami i przerywa po upływie limitu czasu zgodnie z oczekiwaniami
Czas: 15 minut
Plugins:GenericRandomErrorPlugin, RetryAfterPlugin, LatencyPlugin
Wymagania wstępne:Skonfiguruj Dev Proxy, aplikację .NET korzystającą z Microsoft.Extensions.Http.Resilience

Dodano AddStandardResilienceHandler() do Twojego HttpClient. Skąd wiesz, że to działa? Interfejsy API, które są wywoływane rzadko kończą się niepowodzeniem na żądanie, a testy jednostkowe, które imitują HttpMessageHandler, pomijają potok odporności, który chcesz przetestować.

Dev Proxy znajduje się między Twoją aplikacją a interfejsem API. Zwraca błędy i powolne odpowiedzi Twojej aplikacji, więc aplikacja działa bez zmian, a moduł obsługi odporności reaguje na rzeczywiste odpowiedzi HTTP. Widzisz każdą próbę w danych wyjściowych Dev Proxy.

Co robi standardowy moduł obsługi odporności

Przed przetestowaniem dowiedz się, czego się spodziewać. Przy użyciu opcji domyślnych: AddStandardResilienceHandler()

Behavior Wartość domyślna
Ponawianie prób HTTP 500 i wyższe, 408, 429, HttpRequestException i TimeoutRejectedException
Liczba ponownych prób 3, z wykładniczym wydłużaniem odstępów i losowym opóźnieniem, począwszy od 2 sekund
Retry-After Nagłówka Zaszczycony. Procedura obsługi czeka przez czas, o który prosi interfejs API.
Przekroczenie czasu próby 10 sekund na próbę
Łączny limit czasu 30 sekund dla żądania, w tym wszystkie ponowienia prób

Aby uzyskać pełną listę strategii i ich wartości domyślnych, zobacz Domyślne ustawienia standardowej procedury obsługi odporności.

Kieruj aplikację przez Dev Proxy

.NET używa systemowego serwera proxy, więc po uruchomieniu Dev Proxy przechwytuje on żądania aplikacji bez zmian w kodzie. Aby uzyskać więcej informacji, zobacz Use Dev Proxy with .NET applications.

Symuluj błędy przejściowe

Utwórz konfigurację serwera proxy deweloperskiego, która powoduje niepowodzenie żądań do interfejsu API, z błędami, które moduł obsługi ponawia. W tym przykładzie użyto https://api.contoso.com. Zastąp go adresem URL interfejsu API, który wywołuje Twoja aplikacja.

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

Dodaj element RetryAfterPlugin przed elementem GenericRandomErrorPlugin w pliku konfiguracji. Jeśli dodasz to później, GenericRandomErrorPlugin spowoduje niepowodzenie żądania, zanim RetryAfterPlugin będzie mogło to sprawdzić.

W pliku errors zdefiniuj odpowiedź ograniczania liczby żądań i dwa błędy serwera. Wartość @dynamic ustawia nagłówek Retry-After i informuje RetryAfterPlugin, aby sprawdzić, czy aplikacja czeka tak długo, zanim ponownie wywoła interfejs API.

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

Uruchom Dev Proxy i uruchom aplikację.

devproxy --config-file devproxyrc.json

Przy 50% współczynniku awarii większość żądań kończy się powodzeniem po jednej lub dwóch ponownych próbach. W danych wyjściowych Dev Proxy sprawdź, czy:

  • Po odpowiedzi 429 kolejna próba tego samego adresu URL nastąpi po czasie podanym w Retry-After. Domyślnie Dev Proxy używa 5 sekund. Jeśli aplikacja wywołuje interfejs API za wcześnie, RetryAfterPlugin zgłasza to i dławi żądanie.
  • Aplikacja nie wysyła więcej prób niż skonfigurowano.
  • Żądania, których nie chcesz ponawiać, takie jak POST, które tworzy rekord, są wysyłane tylko raz. Aby je wykluczyć, wywołaj options.Retry.DisableForUnsafeHttpMethods() lub options.Retry.DisableFor(...).

Sprawdź, co się stanie po zakończeniu ponownych prób

Ponowne próby ukrywają krótkotrwałe awarie. Musisz również wiedzieć, co aplikacja robi, gdy interfejs API wciąż zawodzi. Uruchom serwer proxy dev ze współczynnikiem awarii 100%:

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

Dev Proxy pokazuje 4 próby dla każdego żądania: oryginalne żądanie i 3 ponowne próby. Po ostatnim ponowieniu próby standardowy moduł obsługi nie zgłasza wyjątku. Zwraca on ostatnią odpowiedź o błędzie do twojego kodu. Sprawdź, co twoja aplikacja z tym robi. Na przykład EnsureSuccessStatusCode() zgłasza wyjątek HttpRequestException, a GetStringAsync() również zgłasza wyjątek. Upewnij się, że aplikacja wyświetla przydatny komunikat lub stosuje rozwiązanie zastępcze, zamiast ulegała awarii lub wyświetlała ogólny błąd.

Limity czasu testów

Powolne interfejsy API wyzwalają inną ścieżkę w procedurze obsługi. Aby go przetestować, użyj LatencyPlugin, aby opóźnić odpowiedzi poza 10-sekundowy limit czasu próby.

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

Uruchom Dev Proxy i uruchom aplikację. Każda próba trwa dłużej niż 10 sekund, więc limit czasu próby anuluje ją i procedura obsługi ponawia próbę. Po 30 sekundach łączny limit czasu anuluje żądanie, a Twój kod otrzymuje wartość TimeoutRejectedException. Sprawdź, czy aplikacja przechwytuje ją i informuje użytkownika o tym, co się stało.

Wskazówka

Aby przetestować te same scenariusze przy użyciu własnych ustawień odporności, zmień wartości w wywołaniu AddStandardResilienceHandler(options => ...) i ponownie uruchom te same konfiguracje Dev Proxy.

Następny krok

Dowiedz się więcej o symulowaniu ograniczania przepustowości w dowolnym interfejsie API.

Informacje dodatkowe