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. Może to poprawić stabilność i odporność aplikacji.

Kontekst i problem

W środowisku rozproszonym wywołania do zdalnych zasobów i usług mogą zakończyć się niepowodzeniem z powodu przejściowych błędów, takich jak wolne połączenie sieciowe, przekroczenie limitu czasu lub nadmierne przydzielenie lub tymczasowa niedostępność zasobów. 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 takiej sytuacji ciągłe ponawianie próby wykonania operacji, która ma niewielkie szanse na powodzenie, może nie mieć sensu, a lepszym rozwiązaniem jest szybkie uznanie operacji za zakończoną niepowodzeniem i odpowiednie obsłużenie tego błędu przez aplikację.

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 operacja, która wywołuje usługę, może mieć skonfigurowany limit czasu, po upływie którego brak odpowiedzi ze strony usługi spowoduje zwrócenie komunikatu o błędzie. Jednak ta strategia może spowodować zablokowanie współbieżnych żądań do tej samej operacji do momentu upłynięcia limitu czasu. Te zablokowane żądania mogą blokować krytyczne zasoby systemu, na przykład pamięć, wątki czy połączenia bazy danych. To może prowadzić do wyczerpania tych zasobów, a w efekcie do awarii innych, również niezwią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. Należy pamiętać, że ustawienie krótszego limitu czasu może pomóc w rozwiązaniu problemu, ale limit czasu nie powinien być zbyt krótki — w przeciwnym razie operacja zakończy się niepowodzeniem w większości przypadków, nawet jeśli żądanie do usługi mogłoby zostać pomyślnie zrealizowane.

Rozwiązanie

Wzorzec wyłącznika, spopularyzowany przez Michaela Nygarda w książceRelease It!, może zapobiec wielokrotnemu powtarzaniu przez aplikację prób wykonania operacji, która prawdopodobnie się nie powiedzie. 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. Powinien on monitorować liczbę ostatnich niepowodzeń i na jej podstawie decydować, czy umożliwić kontynuowanie działania 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 rozpoczyna odmierzanie limitu czasu, a po jego upływie przechodzi w stan połowicznie rozwarty.

    Użycie czasomierza daje systemowi czas na rozwiązanie problemu, który spowodował awarię, przed umożliwieniem aplikacji ponownej próby wykonania 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 nie powiedzie się, wyłącznik zakłada, że błąd jest nadal obecny, więc przywraca stan otwarcia i ponownie uruchamia czasomierz limitu czasu, aby dać systemowi kolejny okres czasu w celu odzyskania 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. Dzięki temu wyłącznik nie przechodzi w stan rozwarty, jeśli niepowodzenia występują okazjonalnie. 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. Można na przykład zastosować wzrastający limit czasu wyłącznika. Początkowo wyłącznik może przechodzić w stan rozwarty na kilka sekund, a następnie, jeśli problem nie zostanie rozwiązany, na kilka minut i tak dalej. 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.

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 wywoływanych w przypadku, gdy 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.

Rodzaje wyjątków. Żądanie może zakończyć się niepowodzeniem z wielu powodów, ale niektóre z nich wskazują na poważniejszą awarię niż inne. Żądanie może na przykład nie powieść się, jeśli zdalna usługa uległa awarii i odzyska sprawność po kilku minutach, lub jeśli tymczasowe przeciążenie usługi spowodowało przekroczenie limitu czasu. Wyłącznik może być w stanie sprawdzić typ występujących wyjątków i dostosować do nich strategię działania. Na przykład przejście w stan rozwarty może następować po większej liczbie wyjątków spowodowanych przekroczeniem limitu czasu, a po mniejszej liczbie niepowodzeń spowodowanych całkowitą niedostępnością usługi.

Rejestrowanie. Wyłącznik powinien rejestrować wszystkie żądania zakończone niepowodzeniem (a czasem także żądania zakończone powodzeniem), aby umożliwić administratorowi monitorowanie kondycji operacji.

Możliwość odzyskiwania. Należy skonfigurować wyłącznik odpowiednio do prawdopodobnego wzorca odzyskiwania chronionej operacji. 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.

Sprawdzanie operacji zakończonych niepowodzeniem. Przełącznik w stanie rozwartym może też nie używać czasomierza do określania, kiedy przejść do stanu połowicznie rozwartego — a zamiast tego okresowo wysyłać polecenie ping do zdalnych usług lub zasobów w celu określenia, czy są już dostępne. 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ęcie. W systemie, w którym czasy odzyskiwania po nieudanych operacjach znacząco się różnią, warto uwzględnić opcję ręcznego resetowania, która umożliwi administratorowi zwarcie wyłącznika i zresetowanie licznika awarii. Analogicznie, administrator może wymusić przejście wyłącznika do stanu rozwartego (i ponowne uruchomienie czasomierza limitu czasu), jeśli operacja chroniona przez wyłącznik jest tymczasowo niedostępna.

Współbieżność. Wiele współbieżnych wystąpień aplikacji może uzyskiwać dostęp do tego samego licznika. 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ść, używając jednego wyłącznika dla jednego typu zasobów, jeśli występuje wielu niezależnych dostawców podstawowych. 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łączanie. Czasami odpowiedź zwracana w wyniku błędu może zawierać dostatecznie dużo informacji, aby wyłącznik przełączył się natychmiast w stan rozwarty i pozostał w nim 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.

Uwaga

Usługa może zwrócić błąd HTTP 429 (Zbyt wiele żądań), jeśli klient ma ograniczoną przepustowość, lub błąd HTTP 503 (Usługa niedostępna), jeśli usługa jest aktualnie niedostępna. Odpowiedź może zawierać dodatkowe informacje, takie jak przewidywana długość opóźnienia.

Powtarzanie nieudanych żądań. W stanie rozwartym wyłącznik może nie tylko wywoływać natychmiastowe niepowodzenie, ale także rejestrować szczegóły poszczególnych żądań w dzienniku i planować ich powtórzenie, gdy zdalne zasoby lub usługi staną się znów dostępne.

Nieprawidłowe przekroczenie limitu czasu w przypadku usług zewnętrznych. Wyłącznik może nie być w stanie w pełni chronić aplikacji przed operacjami, które ulegają awarii w usługach zewnętrznych ze skonfigurowanym długim limitem czasu. Jeśli limit czasu jest zbyt długi, wątek, w którym działa wyłącznik, może zostać zablokowany na długi czas, zanim wyłącznik wskaże, że operacja zakończyła się niepowodzeniem. 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.

Kiedy używać tego wzorca

Użyj tego wzorca, aby:

  • Zapobiec wielokrotnemu powtarzaniu przez aplikację prób wywołania zdalnej usługi lub uzyskania dostępu do udostępnionego zasobu, jeśli istnieje wysokie prawdopodobieństwo, że ta operacja się nie powiedzie.

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.

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.

Przykład

Aplikacja internetowa zawiera kilka stron wypełnianych danymi pobieranymi z usługi zewnętrznej. Jeśli w systemie wdrożono minimalne buforowanie, większość trafień na tych stronach spowoduje komunikację dwustronną z usługą. Połączenia z aplikacji internetowej do usługi mogą mieć skonfigurowany limit czasu (zwykle jest to 60 sekund), a jeśli usługa nie odpowie w tym czasie, logika poszczególnych stron założy, że usługa jest niedostępna, i zwróci wyjątek.

Jeśli jednak usługa ulegnie awarii, a system będzie bardzo obciążony, użytkownicy mogą być zmuszeni do odczekania 60 sekund, zanim wystąpi wyjątek. To może w końcu doprowadzić do wyczerpania zasobów takich jak pamięć, połączenia i wątki, co uniemożliwi innym użytkownikom połączenie się z systemem, nawet jeśli korzystają ze stron pobierających dane z usługi.

Skalowanie systemu przez dodawanie kolejnych serwerów internetowych i wdrożenie równoważenia obciążenia może opóźnić moment wyczerpania zasobów, ale nie rozwiąże problemu, ponieważ żądania użytkowników nadal nie będą odpowiadać, a ostatecznie mogą zostać wyczerpane zasoby wszystkich serwerów.

Opakowanie logiki łączenia z usługą i pobierania danych przy użyciu wyłącznika może pomóc w rozwiązaniu tego problemu i lepszym reagowaniu na awarię usługi. Żądania użytkowników nadal będą kończyć się niepowodzeniem, ale będzie się to działo szybciej, a zasoby nie będą blokowane.

Klasa CircuitBreaker przechowuje informacje o stanie wyłącznika w obiekcie, który wdraża interfejs ICircuitBreakerStateStore pokazany w poniższym kodzie.

interface ICircuitBreakerStateStore
{
  CircuitBreakerStateEnum State { get; }

  Exception LastException { get; }

  DateTime LastStateChangedDateUtc { get; }

  void Trip(Exception ex);

  void Reset();

  void HalfOpen();

  bool IsClosed { get; }
}

Właściwość State wskazuje aktualny stan wyłącznika i ma wartość Open, HalfOpen lub Closed zgodnie z definicją w wyliczeniu CircuitBreakerStateEnum. Właściwość IsClosed powinna mieć wartość true, jeśli wyłącznik jest zwarty, a wartość false, jeśli jest rozwarty lub połowicznie rozwarty. Metoda Trip przełącza wyłącznik do stanu rozwartego i rejestruje wyjątek, który spowodował zmianę stanu, wraz datą i czasem jego wystąpienia. Właściwości LastException i LastStateChangedDateUtc zwracają te informacje. Metoda Reset powoduje zwarcie włącznika, a metoda HalfOpen ustawia go w pozycji połowicznie rozwartej.

Klasa InMemoryCircuitBreakerStateStore w tym przykładzie zawiera wdrożenie interfejsu ICircuitBreakerStateStore. Klasa CircuitBreaker tworzy wystąpienie tej klasy w celu przechowywania stanu wyłącznika.

Metoda ExecuteAction w klasie CircuitBreaker opakowuje operację, określoną jako delegat Action. Jeśli wyłącznik jest zwarty, polecenie ExecuteAction wywołuje delegata Action. W przypadku niepowodzenia operacji procedura obsługi wyjątków wywołuje polecenie TrackException, które ustawia wyłącznik w pozycji rozwartej. Poniższy kod przykładowy przedstawia ten przepływ.

public class CircuitBreaker
{
  private readonly ICircuitBreakerStateStore stateStore =
    CircuitBreakerStateStoreFactory.GetCircuitBreakerStateStore();

  private readonly object halfOpenSyncObject = new object ();
  ...
  public bool IsClosed { get { return stateStore.IsClosed; } }

  public bool IsOpen { get { return !IsClosed; } }

  public void ExecuteAction(Action action)
  {
    ...
    if (IsOpen)
    {
      // The circuit breaker is Open.
      ... (see code sample below for details)
    }

    // The circuit breaker is Closed, execute the action.
    try
    {
      action();
    }
    catch (Exception ex)
    {
      // If an exception still occurs here, simply
      // retrip the breaker immediately.
      this.TrackException(ex);

      // Throw the exception so that the caller can tell
      // the type of exception that was thrown.
      throw;
    }
  }

  private void TrackException(Exception ex)
  {
    // For simplicity in this example, open the circuit breaker on the first exception.
    // In reality this would be more complex. A certain type of exception, such as one
    // that indicates a service is offline, might trip the circuit breaker immediately.
    // Alternatively it might count exceptions locally or across multiple instances and
    // use this value over time, or the exception/success ratio based on the exception
    // types, to open the circuit breaker.
    this.stateStore.Trip(ex);
  }
}

Następujący przykład pokazuje kod (pominięty w poprzednim przykładzie), który jest wykonywany, gdy wyłącznik nie jest zwarty. Najpierw sprawdza on, czy wyłącznik był rozwarty przez okres dłuższy niż określony przez pole lokalne OpenToHalfOpenWaitTime w klasie CircuitBreaker. Jeśli tak, metoda ExecuteAction ustawia wyłącznik w pozycji połowicznie rozwartej, a następnie próbuje wykonać operację określoną przez delegata Action.

Jeśli operacja powiedzie się, wyłącznik jest resetowany do stanu zwartego. Jeśli operacja nie powiedzie się, wyłącznik wraca do stanu rozwartego, a czas wystąpienia wyjątku zostaje zaktualizowany, tak aby przed próbą ponownego wykonania operacji przez wyłącznik znów upłynął określony limit czasu.

Jeśli wyłącznik był rozwarty przez czas krótszy niż wartość OpenToHalfOpenWaitTime, metoda ExecuteAction po prostu wywołuje wyjątek CircuitBreakerOpenException i zwraca błąd, który spowodował przejście wyłącznika do stanu rozwartego.

Dodatkowo stosowana jest blokada, która ma zapobiega próbie wykonania współbieżnych wywołań do operacji, gdy wyłącznik jest w pozycji połowicznie rozwartej. Współbieżne wywołania operacji będą obsługiwane tak, jakby wyłącznik był rozwarty — zakończą się niepowodzeniem i zwróceniem wyjątku, zgodnie z opisem w dalszej części.

    ...
    if (IsOpen)
    {
      // The circuit breaker is Open. Check if the Open timeout has expired.
      // If it has, set the state to HalfOpen. Another approach might be to
      // check for the HalfOpen state that had be set by some other operation.
      if (stateStore.LastStateChangedDateUtc + OpenToHalfOpenWaitTime < DateTime.UtcNow)
      {
        // The Open timeout has expired. Allow one operation to execute. Note that, in
        // this example, the circuit breaker is set to HalfOpen after being
        // in the Open state for some period of time. An alternative would be to set
        // this using some other approach such as a timer, test method, manually, and
        // so on, and check the state here to determine how to handle execution
        // of the action.
        // Limit the number of threads to be executed when the breaker is HalfOpen.
        // An alternative would be to use a more complex approach to determine which
        // threads or how many are allowed to execute, or to execute a simple test
        // method instead.
        bool lockTaken = false;
        try
        {
          Monitor.TryEnter(halfOpenSyncObject, ref lockTaken);
          if (lockTaken)
          {
            // Set the circuit breaker state to HalfOpen.
            stateStore.HalfOpen();

            // Attempt the operation.
            action();

            // If this action succeeds, reset the state and allow other operations.
            // In reality, instead of immediately returning to the Closed state, a counter
            // here would record the number of successful operations and return the
            // circuit breaker to the Closed state only after a specified number succeed.
            this.stateStore.Reset();
            return;
          }
        }
        catch (Exception ex)
        {
          // If there's still an exception, trip the breaker again immediately.
          this.stateStore.Trip(ex);

          // Throw the exception so that the caller knows which exception occurred.
          throw;
        }
        finally
        {
          if (lockTaken)
          {
            Monitor.Exit(halfOpenSyncObject);
          }
        }
      }
      // The Open timeout hasn't yet expired. Throw a CircuitBreakerOpen exception to
      // inform the caller that the call was not actually attempted,
      // and return the most recent exception received.
      throw new CircuitBreakerOpenException(stateStore.LastException);
    }
    ...

Aby użyć obiektu CircuitBreaker w celu ochrony operacji, aplikacja tworzy wystąpienie klasy CircuitBreaker i wywołuje metodę ExecuteAction, określając w parametrze, jaka operacja ma być wykonana. Aplikacja powinna być przygotowana do wykrycia wyjątku CircuitBreakerOpenException, jeśli operacja nie powiedzie się z powodu rozwarcia wyłącznika. Poniżej znajduje się kod przykładowy:

var breaker = new CircuitBreaker();

try
{
  breaker.ExecuteAction(() =>
  {
    // Operation protected by the circuit breaker.
    ...
  });
}
catch (CircuitBreakerOpenException ex)
{
  // Perform some different action when the breaker is open.
  // Last exception details are in the inner exception.
  ...
}
catch (Exception ex)
{
  ...
}

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.