Wzorzec wyłącznika

Azure

Umożliwia obsługę błędów, których naprawienie może potrwać zmienną ilość czasu podczas nawiązywania połączenia ze zdalną usługą lub zasobem. Ten wzorzec może poprawić stabilność i odporność aplikacji.

Kontekst i problem

W środowisku rozproszonym wywołania zasobów zdalnych i usług mogą zakończyć się niepowodzeniem z powodu przejściowych błędów, takich jak wolne połączenia sieciowe, przekroczenia limitu czasu lub nadmiernie zatwierdzane lub tymczasowo niedostępne. Takie błędy zwykle w krótkim czasie korygują się same, ale niezawodna aplikacja w chmurze powinna być przygotowana do obsługi tych błędów przez wprowadzenie odpowiedniej strategii, na przykład tej opisanej w temacie Retry pattern (Wzorzec ponawiania).

Może jednak zdarzyć się, że błędy spowodowane są nieprzewidzianymi zdarzeniami, a ich naprawa może potrwać dłużej. Takie błędy mogą mieć różny stopień ważności — od częściowej utraty łączności do całkowitej awarii usługi. W takich sytuacjach może być bezcelowe, aby aplikacja stale ponawiała próbę wykonania operacji, która jest mało prawdopodobna, a zamiast tego aplikacja powinna szybko zaakceptować, że operacja zakończyła się niepowodzeniem i odpowiednio obsłuży tę awarię.

Co więcej jeśli usługa jest bardzo obciążona, awaria jednej części systemu może doprowadzić do kolejnych awarii. Na przykład można skonfigurować operację, która wywołuje usługę w celu zaimplementowania limitu czasu, i odpowiedzieć z komunikatem o błędzie, jeśli usługa nie odpowie w tym okresie. Jednak ta strategia może spowodować zablokowanie wielu współbieżnych żądań do tej samej operacji do momentu wygaśnięcia limitu czasu. Te zablokowane żądania mogą blokować krytyczne zasoby systemu, na przykład pamięć, wątki czy połączenia bazy danych. W związku z tym te zasoby mogą zostać wyczerpane, powodując awarię innych, prawdopodobnie niepowiązanych części systemu, które muszą korzystać z tych samych zasobów. W takich sytuacjach lepszym rozwiązaniem jest natychmiastowe zakończenie operacji niepowodzeniem i próba wywołania usługi tylko wówczas, gdy to działanie ma szansę na powodzenie. Ustawienie krótszego limitu czasu może pomóc rozwiązać ten problem, ale przekroczenie limitu czasu nie powinno być tak krótkie, że operacja kończy się niepowodzeniem przez większość czasu, nawet jeśli żądanie do usługi zakończy się pomyślnie.

Rozwiązanie

Wzorzec wyłącznika może uniemożliwić aplikacji wielokrotne wykonywanie operacji, która prawdopodobnie zakończy się niepowodzeniem. Umożliwia aplikacji dalsze działanie bez oczekiwania na naprawienie błędu i marnowania cykli procesora CPU, do czasu określenia, czy błąd jest długotrwały. Wzorzec wyłącznika umożliwia również aplikacji wykrycie, czy błąd został naprawiony. Jeśli wydaje się, że problem został naprawiony, aplikacja może spróbować ponownie wywołać operację.

Wzorzec wyłącznika służy do innych celów niż wzorzec ponawiania. Wzorzec ponawiania umożliwia aplikacji ponowienie próby wykonania operacji przy założeniu, że operacja się powiedzie. Wzorzec wyłącznika zapobiega wykonywaniu przez aplikację operacji, która prawdopodobnie się nie powiedzie. Aplikacja może korzystać z obu wzorców, używając wzorca ponawiania do wywołania operacji za pośrednictwem wyłącznika. Jednak logika ponawiania powinna uwzględniać ewentualne wyjątki zwracane przez wyłącznik i przerwać ponawianie prób, jeśli wyłącznik wskazuje, że błąd nie jest przejściowy.

Wyłącznik działa jako serwer proxy dla operacji, które mogą zakończyć się niepowodzeniem. Serwer proxy powinien monitorować liczbę ostatnich awarii i użyć tych informacji, aby zdecydować, czy zezwolić na kontynuowanie operacji, czy natychmiast zwrócić wyjątek.

Ten serwer proxy może być automatem stanów z następującymi stanami, które naśladują funkcje wyłącznika elektrycznego:

  • Zwarty: żądanie aplikacji jest kierowane do operacji. Serwer proxy przechowuje liczbę ostatnich awarii i zwiększa ją, jeśli wywołanie do operacji nie powiedzie się. Jeśli liczba ostatnich awarii przekroczy określony próg we wskazanym czasie, serwer proxy przechodzi w stan rozwarty. W tym momencie serwer proxy uruchamia czasomierz przekroczenia limitu czasu, a po wygaśnięciu tego czasomierza serwer proxy zostanie umieszczony w stanie Half-Open.

    Celem czasomierza limitu czasu jest nadanie systemowi czasu, aby rozwiązać problem, który spowodował awarię przed zezwoleniem aplikacji na ponowne wykonanie operacji.

  • Rozwarty: żądanie aplikacji natychmiast kończy się niepowodzeniem, a do aplikacji zwracany jest wyjątek.

  • Połowicznie rozwarty: umożliwia przesłanie dalej ograniczonej liczby żądań aplikacji w celu wywołania operacji. Jeśli te żądania powiodą się, zakłada się, że błąd, który poprzednio powodował awarię, został naprawiony, a wyłącznik zostaje przełączony do stanu zwartego (licznik awarii zostaje zresetowany). Jeśli jakiekolwiek żądanie zakończy się niepowodzeniem, wyłącznik zakłada, że błąd jest nadal obecny, aby przywrócić stan Otwórz i ponownie uruchomi czasomierz limitu czasu, aby dać systemowi kolejny okres czasu na odzyskanie sprawności po awarii.

    Stan połowicznie rozwarty zapobiega nagłemu napływowi żądań do odzyskiwanej usługi. Odzyskana usługa może być w stanie obsłużyć ograniczoną ilość żądań przed całkowitym rozwiązaniem problemu, ale duże obciążenie w trakcie odzyskiwania może spowodować, że usługa ponownie przekroczy limit czasu lub ulegnie awarii.

Stany wyłącznika

Rysunek przedstawia zależny od czasu licznik niepowodzeń używany, gdy wyłącznik znajduje się w stanie zwartym. Jest on automatycznie resetowany w regularnych odstępach czasu. Ten projekt pomaga zapobiec wejściu wyłącznika w stan Otwórz, jeśli wystąpią sporadyczne awarie. Próg błędów powodujący przełączenie wyłącznika w stan rozwarty jest osiągany dopiero po wystąpieniu określonej liczby awarii w określonym przedziale czasu. W stanie połowicznie rozwartym licznik rejestruje liczbę udanych prób wywołania operacji. Wyłącznik wraca do stanu zwartego, gdy określona liczba kolejnych wywołań operacji zakończy się pomyślnie. W przypadku niepowodzenia jakiegokolwiek wywołania wyłącznik natychmiast przechodzi w stan rozwarty, a przy następnym przejściu w stan połowicznie rozwarty licznik udanych operacji jest resetowany.

Sposób odzyskiwania systemu jest obsługiwany zewnętrznie, na przykład przez przywrócenie lub ponowne uruchomienie składnika, który uległ awarii, lub naprawę połączenia sieciowego.

Wzorzec wyłącznika zapewnia stabilność podczas odzyskiwania systemu po awarii i minimalizuje negatywny wpływ na wydajność. Umożliwia utrzymanie czasu odpowiedzi systemu dzięki szybkiemu odrzuceniu żądania operacji, która prawdopodobnie zakończy się niepowodzeniem, zamiast czekać, aż operacja przekroczy limit czasu, lub pozostawić ją bez odpowiedzi. Jeśli wyłącznik wywołuje zdarzenie przy każdej zmianie stanu, można użyć tej informacji do monitorowania kondycji tej części systemu, którą chroni wyłącznik, lub do powiadomienia administratora, gdy wyłącznik przejdzie w stan rozwarty.

Wzorzec można dostosowywać do prawdopodobnych typów błędów. Na przykład można zastosować do wyłącznika zwiększenie limitu czasu. Wyłącznik można umieścić w stanie Otwórz przez kilka sekund, a następnie, jeśli awaria nie została rozwiązana, zwiększ limit czasu do kilku minut itd. W niektórych przypadkach lepszym rozwiązaniem niż zwrócenie błędu i wywołanie wyjątku przez stan rozwarty jest zwrócenie wartości domyślnej znaczącej dla aplikacji.

Uwaga

Tradycyjnie wyłączniki polegały na wstępnie skonfigurowanych progach, takich jak liczba awarii i czas trwania limitu czasu, co skutkuje deterministycznym, ale czasami nieoptymalnym zachowaniem. Jednak techniki adaptacyjne korzystające ze sztucznej inteligencji i uczenia maszynowego mogą dynamicznie dostosowywać progi na podstawie wzorców ruchu w czasie rzeczywistym, anomalii i historycznych współczynników awarii, dzięki czemu wyłącznik jest bardziej odporny i wydajny.

Problemy i kwestie do rozważenia

Podczas podejmowania decyzji o sposobie wdrażania tego wzorca należy rozważyć następujące kwestie:

obsługa wyjątków: aplikacja wywołująca operację za pośrednictwem wyłącznika musi być przygotowana do obsługi wyjątków zgłoszonych, jeśli operacja jest niedostępna. Sposób obsługi wyjątków zależy od aplikacji. Aplikacja może na przykład tymczasowo ograniczyć funkcjonalność, wywołać inną operację w celu wykonania tego samego zadania lub uzyskania tych samych danych albo zgłosić wyjątek użytkownikowi i poprosić go o ponowienie próby później.

Typy wyjątków: Żądanie może zakończyć się niepowodzeniem z wielu powodów, z których niektóre mogą wskazywać na poważniejszy typ awarii niż inne. Na przykład żądanie może zakończyć się niepowodzeniem, ponieważ usługa zdalna uległa awarii i odzyskanie może potrwać kilka minut lub z powodu przekroczenia limitu czasu z powodu tymczasowego przeciążenia usługi. Wyłącznik może być w stanie sprawdzić typ występujących wyjątków i dostosować do nich strategię działania. Może to na przykład wymagać większej liczby wyjątków przekroczenia limitu czasu w celu uruchomienia wyłącznika do stanu Otwórz w porównaniu z liczbą awarii z powodu całkowitej niedostępności usługi.

monitorowanie: Wyłącznik powinien zapewnić wyraźną obserwację zarówno żądań zakończonych niepowodzeniem, jak i pomyślnych, umożliwiając zespołom operacyjnym ocenę kondycji systemu. Użyj śledzenia rozproszonego, aby uzyskać kompleksową widoczność między usługami.

możliwości odzyskiwania: należy skonfigurować wyłącznik tak, aby był zgodny z prawdopodobnym wzorcem odzyskiwania operacji, którą chroni. Na przykład, jeśli wyłącznik pozostaje w stanie rozwartym przez długi czas, może wywoływać wyjątki, nawet jeśli przyczyna niepowodzenia została usunięta. Podobnie zbyt szybkie przełączanie ze stanu rozwartego do stanu połowicznie rozwartego może powodować wahania i pogorszenie czasu odpowiedzi aplikacji.

Testowanie nieudanych operacji: w stanie Otwórz zamiast używać czasomierza do określenia, kiedy należy przełączyć się na stan half-Open, wyłącznik może zamiast tego okresowo wysyłać polecenia ping do zdalnej usługi lub zasobu, aby określić, czy jest ponownie dostępny. Polecenie ping może by próbą wywołania operacji wcześniej zakończonej niepowodzeniem, ale może też korzystać ze specjalnej operacji udostępnionej przez usługę zdalną w celu sprawdzenia jej kondycji, tak jak opisano w temacie Health Endpoint Monitoring pattern (Wzorzec monitorowania punktów końcowych kondycji).

Ręczne przesłonięcia: w systemie, w którym czas odzyskiwania operacji zakończonej niepowodzeniem jest bardzo zmienny, korzystne jest zapewnienie opcji ręcznego resetowania, która umożliwia administratorowi zamknięcie wyłącznika (i zresetowanie licznika awarii). Podobnie administrator może wymusić wyłącznik do stanu Otwórz (i ponownie uruchomić czasomierz limitu czasu), jeśli operacja chroniona przez wyłącznik jest tymczasowo niedostępna.

współbieżności: dostęp do tego samego wyłącznika może uzyskać duża liczba współbieżnych wystąpień aplikacji. Wdrożenie nie powinno blokować współbieżnych żądań ani dodawać nadmiernego obciążenia do każdego wywołania operacji.

różnicowanie zasobów: należy zachować ostrożność podczas korzystania z jednego wyłącznika dla jednego typu zasobu, jeśli może istnieć wielu niezależnych dostawców. Na przykład w magazynie danych zawierającym wiele fragmentów, jeden fragmentu może być w pełni dostępny, podczas gdy w innym tymczasowo występuje problem. Jeśli błędy zwracane w tych scenariuszach zostaną scalone, aplikacja może próbować uzyskać dostęp do niektórych fragmentów, nawet jeśli istnieje duże prawdopodobieństwo wystąpienia awarii, a dostęp do innych fragmentów może zostać zablokowany, nawet jeśli istnieje prawdopodobieństwo powodzenia.

przyspieszone wyłączniki: czasami odpowiedź na awarię może zawierać wystarczającą ilość informacji, aby wyłącznik natychmiast się potknął i pozostał z opóźnieniem przez minimalny czas. Na przykład błąd zwrócony przez przeciążony zasób udostępniony może wskazywać, że natychmiastowe ponawianie próby nie jest zalecane, i że aplikacja powinna spróbować ponownie za kilka minut.

wdrożenia w wielu regionach: wyłącznik można zaprojektować na potrzeby wdrożeń w jednym lub wielu regionach. Te ostatnie można zaimplementować przy użyciu globalnych modułów równoważenia obciążenia lub niestandardowych strategii przerywania obwodów obsługujących region, które zapewniają kontrolowany tryb failover, optymalizację opóźnień i zgodność z przepisami.

wyłączniki siatki usługi: wyłączniki mogą być implementowane w warstwie aplikacji lub jako funkcja krzyżowa, abstrakcyjna. Na przykład siatki usług często obsługują przerywanie obwodu jako przyczepki lub jako autonomiczną możliwość bez modyfikowania kodu aplikacji.

Uwaga

Usługa może zwrócić http 429 (zbyt wiele żądań), jeśli ogranicza klienta lub HTTP 503 (usługa niedostępna), jeśli usługa nie jest obecnie dostępna. Odpowiedź może zawierać dodatkowe informacje, takie jak przewidywana długość opóźnienia.

żądania zakończone niepowodzeniem: w stanie Otwórz, a nie po prostu niepowodzeniem, wyłącznik może również zarejestrować szczegóły każdego żądania do dziennika i zorganizować ponowne odtwarzanie tych żądań, gdy zasób zdalny lub usługa stanie się dostępny.

nieodpowiednie limity czasu w usługach zewnętrznych: Wyłącznik może nie być w stanie w pełni chronić aplikacji przed operacjami, które kończą się niepowodzeniem w usługach zewnętrznych skonfigurowanych z długim limitem czasu. Jeśli przekroczenie limitu czasu jest zbyt długie, wątek z wyłącznikiem może zostać zablokowany przez dłuższy czas, zanim wyłącznik wskazuje, że operacja nie powiodła się. W tym czasie wiele innych wystąpień aplikacji może również próbować wywołać usługę za pośrednictwem wyłącznika, blokując wiele wątków, zanim wszystkie żądania zakończą się niepowodzeniem.

dostosowanie do dywersyfikacji zasobów obliczeniowych: wyłączniki powinny uwzględniać różne środowiska obliczeniowe, od bezserwerowych po konteneryzowane obciążenia, w których czynniki takie jak zimne starty i obsługa awarii wpływu na skalowalność. Metody adaptacyjne mogą dynamicznie dostosowywać strategie na podstawie typu obliczeniowego, zapewniając odporność między architekturami heterogenicznymi.

Kiedy używać tego wzorca

Użyj tego wzorca, aby:

  • Aby zapobiec awariom kaskadowym, zatrzymując nadmierne wywołania przez usługę zdalną lub żądania dostępu do udostępnionego zasobu, jeśli te operacje mogą zakończyć się niepowodzeniem.
  • Aby zwiększyć odporność na wiele regionów, inteligentnie rozsyłając ruch na podstawie sygnałów awarii w czasie rzeczywistym.
  • Aby chronić się przed wolnymi zależnościami, pomagając nadążyć za celami poziomu usług (SLO) i uniknąć obniżenia wydajności z powodu usług o wysokim opóźnieniu.
  • Aby obsłużyć sporadyczne problemy z łącznością i zmniejszyć liczbę błędów żądań w środowiskach rozproszonych.

Ten wzorzec nie jest zalecany:

  • Do obsługi dostępu do lokalnych zasobów prywatnych w aplikacji, takich jak struktura danych w pamięci. W takim środowisku użycie wyłącznika zwiększałoby obciążenie systemu.
  • Jako substytut obsługi wyjątków w logice biznesowej aplikacji.
  • Gdy dobrze znane algorytmy ponawiania prób są wystarczające, a zależności są przeznaczone do obsługi mechanizmów ponawiania prób. Zaimplementowanie wyłącznika w aplikacji w tym przypadku może spowodować dodanie niepotrzebnej złożoności do systemu.
  • Podczas oczekiwania na zresetowanie wyłącznika może spowodować niedopuszczalne opóźnienia.
  • Jeśli masz architekturę sterowaną komunikatami lub opartą na zdarzeniach, ponieważ często kierują komunikaty, które nie powiodły się do kolejki utraconych listów (DLQ) na potrzeby ręcznego lub odroczonego przetwarzania. Wbudowana izolacja błędów i mechanizmy ponawiania prób zwykle implementowane w tych projektach są często wystarczające.
  • Jeśli odzyskiwanie po awarii jest zarządzane na poziomie infrastruktury lub platformy, na przykład w przypadku kontroli kondycji globalnych modułów równoważenia obciążenia lub siatki usług, wyłączniki mogą nie być konieczne.

Projekt obciążenia

Architekt powinien ocenić, w jaki sposób wzorzec wyłącznika może być używany w projekcie obciążenia, aby sprostać celom i zasadom opisanym w filarach platformy Azure Well-Architected Framework. Na przykład:

Filar Jak ten wzorzec obsługuje cele filaru
Decyzje projektowe dotyczące niezawodności pomagają obciążeniu stać się odporne na awarię i zapewnić, że zostanie przywrócony do w pełni funkcjonalnego stanu po wystąpieniu awarii. Ten wzorzec uniemożliwia przeciążenie zależności błędów. Możesz również użyć tego wzorca, aby wyzwolić łagodne obniżenie obciążenia. Wyłączniki są często połączone z automatycznym odzyskiwaniem, aby zapewnić zarówno samozaprawianie, jak i samonaprawianie.

- ANALIZA trybu awarii RE:03
- RE:07 Błędy przejściowe
- RE:07 Self-preservation
Wydajność pomagawydajnie sprostać zapotrzebowaniu dzięki optymalizacjom skalowania, danych, kodu. Ten wzorzec pozwala uniknąć podejścia ponawiania próby po błędzie, które może prowadzić do nadmiernego wykorzystania zasobów podczas odzyskiwania zależności, a także przeciążyć wydajność zależności, która próbuje odzyskać.

- PE:07 Kod i infrastruktura
- PE:11 Odpowiedzi na problemy na żywo

Podobnie jak w przypadku każdej decyzji projektowej, należy rozważyć wszelkie kompromisy w stosunku do celów innych filarów, które mogą zostać wprowadzone przy użyciu tego wzorca.

Podczas wdrażania tego wzorca przydatne mogą być następujące wzorce:

  • Wzorzec niezawodnej aplikacji internetowej pokazuje, jak zastosować wzorzec wyłącznika do aplikacji internetowych zbieżnych w chmurze.

  • Wzorzec ponawiania. Opisuje, w jaki sposób aplikacja może obsługiwać przewidywane tymczasowe błędy podczas próby połączenia z usługą lub zasobem sieciowym, w sposób niewidoczny ponawiając operację, która poprzednio zakończyła się niepowodzeniem.

  • Wzorzec monitorowania punktu końcowego kondycji. Wyłącznik może sprawdzić kondycję usługi, wysyłając żądanie do punktu końcowego uwidocznionego przez usługę. Usługa powinna zwrócić informacje wskazujące jej stan.