Problem łańcuchowych awarii
Rozproszone systemy są budowane z wielu usług, które wywołują się nawzajem przez sieć, a sieci są niezawodne. Gdy jedna usługa podrzędna zaczyna odpowiadać powoli lub wcale, każda usługa nadrzędna czekająca na tę odpowiedź jest również dotknięta. Jeśli nic nie przeszkodzi, te usługi utrzymują otwarte połączenia, zajmują wątki i zużywają pamięć podczas oczekiwania na odpowiedź, która może nigdy nie nadejść. Wraz z przybywaniem kolejnych żądań puli dostępnych wątków i połączeń w usłudze wywołującej maleje, aż ta sama staje się bezczynna. To właśnie tak pojedyncza awaria zależności w dużej architekturze może wywołać reakcję łańcuchową, która doprowadzi do wyłączenia usług bez bezpośredniego związku z pierwotnym problemem. Usługa sprawdzania zamówień wywołująca wolną usługę magazynową może doprowadzić do głodzenia całego platformy e-commerce, mimo że systemy płatności i wysyłki nie były wadliwe. Wzór bezpiecznika istnieje specjalnie w celu przerwania tej reakcji łańcuchowej przed jej rozprzestrzenianiem się, działając jako ochronny barierę wokół każdego ryzykownego wywołania sieciowego.
Zamknięty: Normalne Stanowisko
W stanie Zamkniętym wyłącznik obwiedzeniowy zachowuje się tak, jakby go nie było w ogóle. Żądania przechodzą bezpośrednio do usługi znajdującej się dalej, a jedyną rolą wyłącznika jest ciche obserwowanie w tle. Każdego razu, gdy połączenie powiedzie się lub zawiedzie, wyłącznik rejestruje wynik, zwykle utrzymując okno z ostatnich wyników lub licznika błędów. Stan ten jest domyślny i zdrowy, a większość żądań w dobrze działającym systemie spędza tu całe swoje życie. Wyłącznik nie blokuje niczego ani nie wprowadza znaczącej opóźnienia; po prostu prowadzi ewidencję. Ważne jest to, że stan ten definiuje logikę progową: wyłącznik określa współczynnik błędów lub liczbę kolejnych błędów, który, po przekroczeniu sygnalizuje, że zależność znajdująca się dalej nie jest wystarczająco zdrowa, aby zaufać. Dopóki błędy pozostają poniżej tego progu, sporadyczne błędy są traktowane jako normalny szum, a nie jako problem systemowy, a wyłącznik pozostaje w stanie Zamkniętym, umożliwiając przepływ ruchu bez zakłóceń, jednocześnie monitorując stan zdrowia usługi.
Otwarty: Celowe Szybkie Awaria
Gdy błędy przekroczą skonfigurowany próg, wyłącznik przechodzi w stan Otwarty i jego zachowanie ulega całkowitej zmianie. Zamiast przekazywać żądania do problematycznego serwisu, wyłącznik natychmiast odrzuca je, często zwracając odpowiedź zastępczą, wartość z pamięci podręcznej lub jasny komunikat o błędzie, bez nawet próby połączenia sieciowego. To jest sedno wartości tego wzorca: celowe szybkie awaria są lepsze niż powolne awarie. Żądanie, które wyczerpie 30 sekund, nadal zużywa wątek, połączenie i pamięć przez te 30 sekund, a jeśli tysiące żądań jest przetwarzanych w ten sam sposób, zasoby samego wywołującego serwisu zostają wyczerpane, przekształcając go w kolejną ofiarę pierwotnego błędu. Odrzucając żądania natychmiastowo, wyłącznik Otwarty utrzymuje wolne puli wątków i połączeń, zachowuje zdolność do obsługi innego ruchu i zapobiega rozprzestrzenianiu się błędu w górę łańcucha wywołań. Co równie ważne, zatrzymuje również bombardowanie już problematycznego serwisu dodatkowym ruchem, którego nie jest w stanie obsłużyć, dając mu oddech potrzebny do odzyskania równowagi zamiast utrzymywania go pod wodą przez ciągłe próby.”]} {}
paragraphs_pl
Półotwarty: Testowanie Wodzy
Fala morska nie może pozostać otwarta w nieskończoność, ponieważ podległa usługa mogła już się zregenerować, a wyłącznik nadal bezwzględnie odrzuca wszystko. Po upływie skonfigurowanego czasu oczekiwania wyłącznik przechodzi w stan Półotwarty, ostrożne środkowe położenie między całkowitym nieufnością a pełnym zaufaniem. W tym stanie wyłącznik pozwala na przepuszczenie niewielkiej liczby próbnych żądań do usługi podrzędnej, jednocześnie blokując resztę. Wynik tych próbnych żądań określa następną zmianę: jeśli się powiedzie, wyłącznik wnioskuje, że zależność się zregenerowała i wraca do stanu Zamkniętego, przywracając normalny przepływ ruchu i resetując liczniki błędów. Jeśli nawet jedno próbne żądanie zawiedzie, wyłącznik zakłada, że problem nadal występuje, natychmiast przechodzi w stan Otwarty i rozpoczyna ponownie odliczanie czasu oczekiwania przed ponownym próbowaniem.
Przełączniki Obwiedzeniowe w Realnych Architektach Mikrousług
Wzorzec przełącznika obwiedzeniowego przeszedł od teorii do powszechnej praktyki głównie dzięki Netflix, którego biblioteka Hystrix zpopularyzniła go dla flot mikrousług opartych na Javie obsługujących ogromne wolumeny żądań w setkach współzależnych usług. Hystrix otaczała wywołania do zależności konfigurowalnymi progami, mechanizmami awaryjnego odzyskiwania i timeoutami, a jej panel kontrolny umożliwiał obserwację wyłączania obwodów i ich przywracanie w czasie rzeczywistym na całej flocie. Chociaż Netflix wstrzymał Hystrix w tryb konserwacji, to jego pomysły żyją bezpośrednio w resilience4j, lekkiej, modułowej bibliotece, która implementuje te same podstawowe wzorce, przełączniki obwiedzeniowe, ograniczniki przepustowości, ściany ognia i ponowny próbę, zaprojektowanej z myślą o nowoczesnych, reaktywnych aplikacjach Java. Podobne implementacje istnieją w zasadzie we wszystkich ekosystemach, takich jak Polly w .NET oraz opossum w Node.js, a bramki API i sieci usług coraz częściej integrują przełączniki obwiedzeniowe jako zachowanie na poziomie infrastruktury zamiast kodu aplikacji. W każdym przypadku cel jest ten sam: zapobiegać awarii jednej zależności, która spowoduje awarię wszystkiego, co w niej polega, przekształcając potencjalną awarię platformową w kontrolowany i odzyskiwany incydent wpływający tylko na bezpośrednio dotknięty moduł.
Często zadawane pytania
Jak się różni wyłącznik obwodu od prostego mechanizmu ponownego próbowania?
Mechanizm ponownego próbowania stale próbuje tego samego nieudanego wywołania, co może pogorszyć sytuację, zwiększając obciążenie już przeciążonego serwisu. Wyłącznik obwodu natomiast całkowicie przerywa próby wywoływania, gdy liczba błędów przekroczy próg, dając możliwości odzyskania się usłudze docelowej zamiast zalewania jej kolejnymi żądaniami.
Co dzieje się z żądaniami, gdy wyłącznik jest w stanie otwartym?
Żądania są natychmiast odrzucane i nigdy nie docierają do serwisu docelowego, zazwyczaj uruchamiając odpowiedź awaryjną, wynik z pamięci podręcznej lub eleganckie komunikaty o błędach. Zapobiega to długim opóźnieniom i wyczerpaniu zasobów wynikającym z powolnego wygaszania żądań.
Jak zwykle ustalany jest próg awarii?
Większość implementacji wykorzystuje albo liczbę kolejnych nieudanych prób, albo współczynnik błędów obliczony w oknie czasu dla ostatnich żądań, np. uruchamiając wyłącznik, gdy więcej niż 50% ostatnich 20 wywołań zakończyło się niepowodzeniem. Odpowiedni próg zależy od normalnego współczynnika błędów serwisu i tego, jak tolerancyjny ma być system w stosunku do chwilowych zakłóceń.
Dlaczego nie pozwolić żądaniom na naturalne wygaszanie zamiast używać wyłącznika obwodu?
Powolne wygaszanie zużywa wątki, połączenia i pamięć w serwisie wywołującym przez cały czas trwania oczekiwania. Jeśli wiele żądań jest uwięzionych w ten sposób jednocześnie, zasoby samego serwisu mogą zostać wyczerpane, rozprzestrzeniając awarię na usługi, które nigdy nie były uszkodzone.
Czy wzorzec wyłącznika obwodu jest przydatny tylko dla mikroserwisów?
Jest najbardziej związany z mikroserwisami ze względu na dużą liczbę wywołań sieciowych między usługami, ale ten sam mechanizm stosuje się do każdego klienta, który wywołuje niezawodną zależność, w tym połączenia z bazą danych, API od dostawców trzecich, a nawet wywołania między monolitem a systemami zewnętrznymi.
Wypróbuj na żywo
Wszystko powyżej działa bezpośrednio w Twojej przeglądarce — otwórz The Circuit Breaker Pattern: Stopping Cascading Failures in Software Systems i zmieniaj parametry podczas działania. Nic nie jest instalowane ani przesyłane na serwer, cały model działa w jednej karcie.
▶ Otwórz symulację The Circuit Breaker Pattern: Stopping Cascading Failures in Software Systems