Udostępnij za pośrednictwem


Ponów próbę antywzorzec Storm

Gdy usługa jest niedostępna lub zajęta, klienci zbyt często ponawiają próby połączeń, mogą spowodować, że usługa będzie miała trudności z odzyskaniem i może pogorszyć problem. Nie ma również sensu ponawiać próby na zawsze, ponieważ żądania są zwykle ważne tylko przez określony okres czasu.

Opis problemu

W chmurze usługi czasami występują problemy i stają się niedostępne dla klientów lub muszą ograniczać lub ograniczać szybkość swoich klientów. Chociaż dobrym rozwiązaniem jest ponawianie próby ponawiania prób połączenia z usługami, ważne jest, aby nie ponawiać prób zbyt często ani zbyt długo. Ponowne próby w krótkim czasie są mało prawdopodobne, aby odnieść sukces, ponieważ usługi prawdopodobnie nie zostaną odzyskane. Ponadto usługi można umieścić w jeszcze większym stresie, gdy podczas próby odzyskania wielu prób połączenia są podejmowane, a powtarzające się próby połączenia mogą nawet przytłoczyć usługę i pogorszyć podstawowy problem.

Poniższy przykład ilustruje scenariusz, w którym klient łączy się z interfejsem API opartym na serwerze. Jeśli żądanie nie powiedzie się, klient natychmiast ponowi próbę i ponowi próbę na zawsze. Często takie zachowanie jest bardziej subtelne niż w tym przykładzie, ale ta sama zasada ma zastosowanie.

public async Task<string> GetDataFromServer()
{
    while(true)
    {
        var result = await httpClient.GetAsync(string.Format("http://{0}:8080/api/...", hostName));
        if (result.IsSuccessStatusCode) break;
    }

    // ... Process result.
}

Jak rozwiązać ten problem

Aplikacje klienckie powinny postępować zgodnie z najlepszymi rozwiązaniami, aby uniknąć powodowania burzy ponawiania prób.

  • Podaj limit liczby prób ponawiania i nie należy ponawiać próby przez długi czas. Chociaż może wydawać się łatwe do pisania while(true) pętli, prawie na pewno nie chcesz faktycznie ponowić próby przez długi czas, ponieważ sytuacja, która doprowadziła do zainicjowania żądania prawdopodobnie się zmieniła. W większości aplikacji ponawianie próby przez kilka sekund lub minut jest wystarczające.
  • Wstrzymaj się między próbami ponawiania próby. Jeśli usługa jest niedostępna, ponawianie próby natychmiast nie powiedzie się. Stopniowo zwiększa czas oczekiwania między próbami, na przykład przy użyciu strategii wycofywania wykładniczego.
  • Bezproblemowo obsłuż błędy. Jeśli usługa nie odpowiada, zastanów się, czy warto przerwać próbę i zwrócić błąd z powrotem do użytkownika lub wywołującego składnik. Podczas projektowania aplikacji należy wziąć pod uwagę te scenariusze awarii.
  • Rozważ użycie wzorca wyłącznika, który został zaprojektowany specjalnie, aby uniknąć ponawiania prób.
  • Jeśli serwer udostępnia retry-after nagłówek odpowiedzi, upewnij się, że nie próbujesz ponowić próby, dopóki określony okres nie upłynął.
  • Użyj oficjalnych zestawów SDK podczas komunikowania się z usługami platformy Azure. Te zestawy SDK zazwyczaj mają wbudowane zasady ponawiania prób i ochrony przed przyczyną lub współtworzeniem ponawiania prób. Jeśli komunikujesz się z usługą, która nie ma zestawu SDK lub gdzie zestaw SDK nie obsługuje logiki ponawiania prób poprawnie, rozważ użycie biblioteki, takiej jak Polly (dla platformy .NET) lub ponów próbę (dla języka JavaScript), aby prawidłowo obsługiwać logikę ponawiania prób i unikać samodzielnego pisania kodu.
  • Jeśli korzystasz ze środowiska, które go obsługuje, użyj siatki usługi (lub innej warstwy abstrakcji), aby wysyłać wywołania wychodzące. Zazwyczaj te narzędzia, takie jak Dapr, obsługują zasady ponawiania prób i automatycznie stosują najlepsze rozwiązania, takie jak wycofywanie po wielokrotnych próbach. Takie podejście oznacza, że nie musisz samodzielnie pisać kodu ponawiania prób.
  • Rozważ przetwarzanie wsadowe żądań i użycie puli żądań, jeśli są dostępne. Wiele zestawów SDK obsługuje przetwarzanie wsadowe żądań i buforowanie połączeń w Twoim imieniu, co spowoduje zmniejszenie całkowitej liczby prób połączenia wychodzącego wykonywanej przez aplikację, chociaż nadal trzeba zachować ostrożność, aby nie ponawiać prób tych połączeń zbyt często.

Usługi powinny również chronić się przed ponawianiem prób.

  • Dodaj warstwę bramy, aby można było wyłączyć połączenia podczas zdarzenia. Jest to przykład wzorca bulkhead. Platforma Azure udostępnia wiele różnych usług bramy dla różnych typów rozwiązań, takich jak usługa Front Door, Application Gateway i API Management.
  • Ograniczanie żądań w bramie, co gwarantuje, że nie zaakceptujesz tak wielu żądań, że składniki zaplecza nie będą nadal działać.
  • Jeśli ograniczasz przepustowość, wyślij nagłówek z powrotem retry-after , aby ułatwić klientom zrozumienie, kiedy ponownie wykonać połączenia.

Zagadnienia do rozważenia

  • Klienci powinni rozważyć typ zwróconego błędu. Niektóre typy błędów nie wskazują awarii usługi, ale zamiast tego wskazują, że klient wysłał nieprawidłowe żądanie. Jeśli na przykład aplikacja kliencka otrzyma 400 Bad Request odpowiedź na błąd, ponowienie próby tego samego żądania prawdopodobnie nie pomoże, ponieważ serwer informuje o tym, że żądanie jest nieprawidłowe.
  • Klienci powinni wziąć pod uwagę czas, który ma sens, aby ponownie zastosować połączenia. Czas ponawiania próby będzie napędzany przez wymagania biznesowe i to, czy można rozsądnie propagować błąd z powrotem do użytkownika lub wywołującego. W większości aplikacji ponawianie próby przez kilka sekund lub minut jest wystarczające.

Jak wykryć problem

Z perspektywy klienta objawy tego problemu mogą obejmować bardzo długie odpowiedzi lub czasy przetwarzania wraz z telemetrią wskazującą powtarzające się próby ponawiania próby nawiązania połączenia.

Z perspektywy usługi objawy tego problemu mogą obejmować dużą liczbę żądań od jednego klienta w krótkim czasie lub dużą liczbę żądań od jednego klienta podczas odzyskiwania po awarii. Objawy mogą również obejmować trudności podczas odzyskiwania usługi lub trwające kaskadowe awarie usługi bezpośrednio po naprawieniu błędu.

Przykładowa diagnostyka

W poniższych sekcjach przedstawiono jedno podejście do wykrywania potencjalnego burzy ponawiania prób, zarówno po stronie klienta, jak i po stronie usługi.

Identyfikowanie danych telemetrycznych klienta

aplikacja systemu Azure Insights rejestruje dane telemetryczne z aplikacji i udostępnia dane do wykonywania zapytań i wizualizacji. Połączenia wychodzące są śledzone jako zależności, a informacje o nich mogą być dostępne i wykresowane w celu określenia, kiedy klient wykonuje dużą liczbę żądań wychodzących do tej samej usługi.

Poniższy graf został pobrany z karty Metryki w portalu usługi Application Insights i wyświetlił metrykę Błędy zależności podzielone według nazwy zależności zdalnej. Ilustruje to scenariusz, w którym w krótkim czasie wystąpiła duża liczba (ponad 21 000) prób połączenia zakończonych niepowodzeniem.

Zrzut ekranu usługi Application Insights przedstawiający błędy zależności 21 tys. dla jednej zależności w ciągu 30 minut

Identyfikowanie danych telemetrycznych serwera

Aplikacje serwera mogą wykrywać dużą liczbę połączeń z jednego klienta. W poniższym przykładzie usługa Azure Front Door działa jako brama dla aplikacji i została skonfigurowana do rejestrowania wszystkich żądań w obszarze roboczym usługi Log Analytics.

Następujące zapytanie Kusto można wykonać względem usługi Log Analytics. Będzie identyfikować adresy IP klienta, które wysłały dużą liczbę żądań do aplikacji w ciągu ostatniego dnia.

AzureDiagnostics
| where ResourceType == "FRONTDOORS" and Category == "FrontdoorAccessLog"
| where TimeGenerated > ago(1d)
| summarize count() by bin(TimeGenerated, 1h), clientIp_s
| order by count_ desc

Wykonanie tego zapytania podczas ponawiania próby powoduje wyświetlenie dużej liczby prób połączenia z jednego adresu IP.

Zrzut ekranu usługi Log Analytics przedstawiający 81 608 połączeń przychodzących z usługą Front Door z jednego adresu IP w ciągu jednej godziny