Błędy 500, 502, 503 i 504 z interfejsów API: co oznaczają i jak je obsługiwać

Kod statusu z zakresu od 500 do 599 oznacza, że serwer nie zdołał zrealizować żądania, które wyglądało na prawidłowe. Twoje żądanie nie jest problemem, więc wysłanie go ponownie może zadziałać. To, czy należy wysłać go ponownie, zależy od kodu stanu i od tego, co robi żądanie. Aby zapoznać się z definicjami, zobacz RFC 9110, sekcja 15.6.

Co oznacza każdy kod stanu

Kod stanu Co to znaczy Ponów próbę?
500 Internal Server Error Serwer napotkał sytuację, której się nie spodziewał To zależy od interfejsu API. Niektóre interfejsy API, takie jak interfejs API Claude'a, zalecają ponowić żądanie po błędzie 500 ze strategią wykładniczego wydłużania odstępów. Sprawdź dokumenty interfejsu API.
502 Bad Gateway Brama lub serwer proxy otrzymał nieprawidłową odpowiedź z serwera za nim Tak, jeśli żądanie można bezpiecznie ponowić
503 Service Unavailable Serwer jest tymczasowo przeciążony lub wyłączony na potrzeby konserwacji i powinien wrócić do działania po pewnym czasie. Serwer może wysłać nagłówek Retry-After. Tak, po czasie Retry-After, jeśli serwer taki wysłał
504 Gateway Timeout Brama lub serwer proxy nie otrzymały odpowiedzi na czas od serwera nadrzędnego Tak, jeśli żądanie jest bezpieczne do powtórzenia. Brama sieciowa zrezygnowała z oczekiwania, więc nie wiesz, czy serwer wykonał pracę.

Które żądania są bezpieczne, aby ponowić próbę

RFC 9110 określa metodę jako idempotentną, gdy wysłanie tego samego żądania kilka razy ma taki sam efekt, jak wysłanie go raz. GET, HEAD, TRACE, OPTIONS, PUT i DELETE są idempotentne. POST i PATCH nie są. Zgodnie z RFC klient nie powinien automatycznie ponawiać żądania przy użyciu metody innej niż idempotentna, chyba że wie, że żądanie jest idempotentne mimo to, lub może stwierdzić, że serwer nigdy nie zastosował oryginalnego żądania. Aby uzyskać szczegółowe informacje, zobacz Metody idempotentne.

Ponowiona próba POST po 502 lub 504 może utworzyć drugie zamówienie lub wysłać drugą wiadomość e-mail. Niektóre biblioteki ponawiania prób domyślnie ponawiają próbę każdej metody. Na przykład standardowy moduł obsługi odporności platformy .NET ponawia próbę POST, chyba że wywołasz metodę options.Retry.DisableForUnsafeHttpMethods(). Aby uzyskać szczegółowe informacje, zobacz Tworzenie odpornych aplikacji HTTP.

Retry-After na 503

503 może zawierać nagłówek Retry-After. Jego wartość to albo liczba sekund, na przykład 120, albo data HTTP, na przykład Fri, 31 Dec 1999 23:59:59 GMT. Kod musi obsługiwać oba te elementy. Aby uzyskać szczegółowe informacje, zobacz Retry-After.

Zatrzymaj wywoływanie interfejsu API, który nadal kończy się niepowodzeniem

Ponawianie prób pomaga w przypadku krótkotrwałych błędów. Gdy interfejs API nie działa przez kilka minut, ponawianie próby każdego żądania powoduje dodanie obciążenia do serwera, który już zmaga się z problemami, a użytkownicy czekają na każde ponowienie próby, aby zakończyć się niepowodzeniem. Mechanizm circuit breaker monitoruje błędy, a gdy jest ich zbyt wiele, przestaje przez pewien czas wywoływać interfejs API i od razu zwraca błąd. Po tym czasie umożliwia to kilku żądaniom sprawdzenie, czy interfejs API wrócił do działania. Aby uzyskać więcej informacji, zobacz Wzorzec Circuit Breaker. Standardowy program obsługi odporności platformy .NET zawiera wyłącznik otwierany przez 5 sekund, gdy co najmniej 10% żądań kończy się niepowodzeniem w 30-sekundowym oknie z co najmniej 100 żądaniami.

Jak obsługiwać błędy 5xx

  1. Ponów żądania kończące się kodem 502, 503 i 504 tylko w przypadku żądań idempotentnych. W przypadku POST i PATCH spróbuj ponownie tylko wtedy, gdy dokumentacja interfejsu API opisuje sposób, w jaki można bezpiecznie ponowić próbę.
  2. Poczekaj, zanim ponowisz próbę. Użyj Retry-After, gdy serwer go wyśle. W przeciwnym razie użyj wykładniczego opóźnienia ponownych prób z losowym odchyleniem i zatrzymaj się po kilku próbach.
  3. Przeczytaj dokumentację interfejsu API dotyczącą błędu 500. Ponów próbę tylko wtedy, gdy interfejs API mówi, że ponowienie próby jest bezpieczne.
  4. Zatrzymaj wywoływanie interfejsu API, który nadal kończy się niepowodzeniem. Użyj bezpiecznika obwodu, aby aplikacja szybko kończyła działanie, gdy interfejs API wraca do sprawności.
  5. Poinformuj użytkownika o tym, co się stało. Pokaż "usługa ma problemy, spróbuj ponownie później" zamiast ogólnego błędu lub śladu stosu.
const RETRYABLE_STATUS = new Set([502, 503, 504]);
const IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"]);

function retryAfterMs(response) {
  const value = response.headers.get("retry-after");
  if (!value) return null;
  const seconds = Number(value);
  if (Number.isFinite(seconds)) return seconds * 1000;
  const date = Date.parse(value);
  return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
}

export async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  const method = (options.method ?? "GET").toUpperCase();
  const canRetry = IDEMPOTENT_METHODS.has(method);

  for (let attempt = 0; ; attempt++) {
    const response = await fetch(url, options);
    if (!RETRYABLE_STATUS.has(response.status) || !canRetry || attempt === maxRetries) {
      return response;
    }
    await response.body?.cancel();
    const backoff = 2 ** attempt * 1000 + Math.random() * 1000;
    const wait = retryAfterMs(response) ?? backoff;
    await new Promise((resolve) => setTimeout(resolve, wait));
  }
}

Jak przetestować, czy aplikacja obsługuje błędy 5xx

Podczas opracowywania rzadko występuje błąd 5xx i nie można utworzyć interfejsu API na żądanie. Sposób testowania określa to, czy znajdziesz błędy, zanim zrobią to użytkownicy.

Approach Co znajdziesz Co przegapiłeś
Oczekiwanie na środowisko produkcyjne Rzeczywiste awarie Wszystko, dopóki użytkownik go kliknie
Mockuj interfejs API w testach lub pozwól agentowi kodowania napisać mock Czy Twoja gałąź obsługi błędów zostanie uruchomiona Prawdziwy klient HTTP i biblioteka ponawiania prób oraz ile razy faktycznie ponawia próbę. Aplikacja wymaga również przełącznika tylko do testowania, aby uzyskać dostęp do mocka.
Wywołaj rzeczywisty interfejs API i poczekaj na jego awarię Rzeczywiste zachowanie Nie można spowodować awarii interfejsu API na żądanie
Przechwyć rzeczywisty ruch aplikacji i zwracaj błędy 5xx w wybranym tempie Twój prawdziwy klient HTTP, biblioteka ponawiania prób i bezpiecznik obwodu W Twojej aplikacji nic się nie zmienia, więc aplikacja nie testuje Twojego kodu w izolacji. Zachowaj testy jednostkowe do tego.

Wypróbuj ją w swojej aplikacji

Dev Proxy przechwytuje żądania aplikacji i powoduje błędy w części z nich, używając wtyczki GenericRandomErrorPlugin. Aplikacja ciągle wywołuje prawdziwy adres URL. Dodaj wtyczkę do pliku konfiguracji i ustaw jego errorsFile na plik z błędami 5xx. W tym przykładzie użyto https://api.contoso.com. Zastąp go adresem URL interfejsu API, który wywołuje Twoja aplikacja.

Plik: server-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": 500 },
        { "statusCode": 502 },
        {
          "statusCode": 503,
          "headers": [
            { "name": "Retry-After", "value": "10" }
          ]
        },
        { "statusCode": 504 }
      ]
    }
  ]
}

Domyślnie wtyczka powoduje niepowodzenie 50% żądań. Sprawdź w danych wyjściowych serwera proxy deweloperów, czy aplikacja ponawia żądania GET i wysyła każde POST tylko raz. Dev Proxy nie sprawdza, czy aplikacja czeka na odpowiedź 503 Retry-After, więc porównaj czasy żądania samodzielnie. Następnie uruchom usługę Dev Proxy za pomocą --failure-rate 100, aby zobaczyć, co robi twoja aplikacja, gdy wywołania API nadal kończą się niepowodzeniem. Aby uzyskać więcej informacji, zobacz Wskaźnik niepowodzeń żądań zmiany. Aby zainstalować Dev Proxy, zobacz Konfigurowanie Dev Proxy.

Następne kroki

Informacje dodatkowe