
Odporność i wysoka dostępność to duże tematy, dlatego poświęcimy RabbitMQ i Kafka osobne artykuły. Ten artykuł dotyczy RabbitMQ, a następny będzie o Kafka w porównaniu do RabbitMQ. Artykuł jest długi, więc rozsiądź się wygodnie.
Zastanowimy się nad strategiami odporności, spójności oraz wysokiej dostępności (HA), a także kompromisami, które trzeba zaakceptować w każdej strategii. RabbitMQ może działać w klastrze węzłów, przez co klasyfikuje się jako system rozproszony. Gdy mówimy o systemach rozproszonych, często poruszamy tematy spójności i dostępności.
Te pojęcia opisują, jak system zachowuje się w przypadku awarii. Awaria połączenia sieciowego, awaria serwera, awaria dysku twardego, tymczasowa niedostępność serwera z powodu zbierania śmieci, utrata pakietów lub spowolnienie połączenia sieciowego. Wszystko to może prowadzić do utraty danych lub konfliktów. Okazuje się, że praktycznie niemożliwe jest utrzymanie systemu, który jednocześnie byłby całkowicie spójny (bez utraty danych, bez rozbieżności danych) oraz dostępny (akceptującego operacje odczytu i zapisu) we wszystkich scenariuszach awarii.
Zobaczymy, że spójność i dostępność znajdują się na przeciwnych końcach spektrum, a Ty musisz wybrać, w którą stronę optymalizować. Dobrą wiadomością jest to, że z RabbitMQ taki wybór jest możliwy. Masz „nerdowskie” dźwignie, aby przesunąć równowagę w stronę większej spójności lub większej dostępności.
Szczególną uwagę zwrócimy na to, jakie konfiguracje prowadzą do utraty danych z powodu potwierdzonych zapisów. Istnieje łańcuch odpowiedzialności między publikującymi, brokerami i konsumentami. Po tym jak wiadomość zostanie przekazana brokerowi, to jego obowiązek — nie zgubić tej wiadomości. Gdy broker potwierdza publikującemu otrzymanie wiadomości, nie oczekujemy, że zostanie ona utracona. Jednak zobaczymy, że może się to zdarzyć w zależności od konfiguracji Twojego brokera i wydawcy.
Primitives of node resilience
Odporną kolejki/routing
W RabbitMQ istnieją dwa typy kolejek: trwałe (durable) i nietrwałe (non-durable). Wszystkie kolejki są przechowywane w bazie danych Mnesia. Trwałe kolejki są ponownie deklarowane podczas uruchamiania węzła, co oznacza, że przetrwają ponowne uruchomienie, awarię systemu lub serwera (o ile dane są zachowane). Oznacza to, że dopóki deklarujesz routowanie (exchange) i kolejkę jako trwałe, infrastruktura kolejek/routingu powróci do trybu operacyjnego.
Nietrwałe kolejki i routowanie są usuwane podczas ponownego uruchomienia węzła.
Trwałe wiadomości
To, że kolejka jest trwała, nie oznacza, że wszystkie jej wiadomości przetrwają ponowne uruchomienie węzła. Zostaną odzyskane tylko wiadomości, które zostały oznaczone przez publikatora jako trwałe (persistent). Trwałe wiadomości rzeczywiście zwiększają obciążenie brokera, ale jeśli utrata wiadomości jest nieakceptowalna, to nie ma innej opcji.

Rys. 1. Macierz trwałości
Klasteryzacja z lustrzanym odbiciem kolejek
Aby przetrwać utratę brokera, potrzebujemy nadmiarowości. Możemy połączyć kilka węzłów RabbitMQ w klaster, a następnie dodać dodatkową nadmiarowość poprzez replikację kolejek pomiędzy wieloma węzłami. W ten sposób, jeśli jeden węzeł zawiedzie, nie tracimy danych i pozostajemy dostępni.
Lustrzane odbicie kolejki:
- jedna główna kolejka (master), która otrzymuje wszystkie polecenia zapisu i odczytu
- jedno lub więcej luster, które otrzymują wszystkie wiadomości i metadane z głównej kolejki. Te lustra istnieją nie do skalowania, ale wyłącznie dla nadmiarowości.

Rys. 2. Lustrzane odbicie kolejki
Lustrzane odbicie jest ustawiane odpowiednią polityką. Można w niej wybrać współczynnik replikacji oraz nawet węzły, na których powinna się znajdować kolejka. Przykłady:
ha-mode: allha-mode: exactly, ha-params: 2(jeden master i jedno lustro)ha-mode: nodes, ha-params: rabbit@node1, rabbit@node2
Potwierdzenie dla publikatora
Aby uzyskać spójną rejestrację, wymagane są potwierdzenia od wydawcy (Publisher Confirms). Bez nich istnieje ryzyko utraty wiadomości. Potwierdzenie jest wysyłane do wydawcy po zapisaniu wiadomości na dysku. RabbitMQ zapisuje wiadomości na dysku nie w momencie odbioru, ale w regularnych odstępach, co kilka setek milisekund. Gdy kolejka jest replikowana, potwierdzenie jest wysyłane dopiero po tym, jak wszystkie repliki również zapisały swoją kopię wiadomości na dysku. Oznacza to, że użycie potwierdzeń dodaje opóźnienie, ale jeśli bezpieczeństwo danych jest ważne, są one niezbędne.
Odporna kolejka
Gdy broker kończy pracę lub ulega awarii, wszystkie główne kolejki (master) na tym węźle przestają działać. Następnie klaster wybiera najstarszą replikę każdego mastera i promuje ją do nowego mastera.

Rys. 3. Kilka replikowanych kolejek i ich polityki
Broker 3 ulega awarii. Zauważ, że replikacja Kolejki C na Brokerze 2 jest promowana do mastera. Zauważ także, że dla Kolejki C utworzono nową replikę na Brokerze 1. RabbitMQ zawsze stara się utrzymać wskaźnik replikacji określony w twoich politykach.

Rys. 4. Broker 3 ulega awarii, co powoduje awarię kolejki C
Ulega awarii kolejny Broker 1! Został nam tylko jeden broker. Replika Kolejki B jest promowana do mastera.

Rys. 5
Przywróciliśmy Brokera 1. Niezależnie od tego, jak pomyślnie dane przetrwały utratę i przywrócenie brokera, wszystkie replikowane wiadomości kolejki są odrzucane podczas ponownego uruchamiania. Warto to zaznaczyć, gdyż będą tego konsekwencje. Wkrótce przyjrzymy się tym konsekwencjom. W związku z tym Broker 1 znów jest członkiem klastra, a klaster stara się przestrzegać polityk i dlatego tworzy replikę na Brokerze 1.
W tym przypadku utrata Brokera 1 była całkowita, jak i danych, dlatego niezreplikowana Kolejka B została całkowicie utracona.

Rys. 6. Broker 1 wraca do działania
Broker 3 wrócił do działania, więc kolejki A i B odzyskują utworzone na nim lustra, aby zaspokoić swoje polityki HA. Ale teraz wszystkie główne kolejki są na jednym węźle! To nie jest idealne, lepiej byłoby równomierne rozłożenie między węzłami. Niestety, nie ma tutaj szczególnych opcji do przeważenia masterów. Wrócimy do tego problemu później, ponieważ najpierw musimy rozważyć synchronizację kolejki.

Rys. 7. Broker 3 wraca do działania. Wszystkie główne kolejki na jednym węźle!
W ten sposób powinieneś już mieć wyobrażenie, jak lustra zapewniają nadmiarowość i odporność na awarie. Gwarantuje to dostępność w przypadku awarii jednego węzła i chroni przed utratą danych. Ale nie skończyliśmy jeszcze, ponieważ w rzeczywistości wszystko jest znacznie bardziej skomplikowane.
Synchronizacja
Podczas tworzenia nowego lustra wszystkie nowe wiadomości zawsze będą replikowane na to lustro i wszelkie inne. Jeśli chodzi o istniejące dane w głównej kolejce, możemy je zreplikować w nowe lustro, które staje się pełną kopią mastera. Możemy również nie replikować istniejących wiadomości i pozwolić głównej kolejce i nowemu lustrze synchronizować się w czasie, w którym nowe wiadomości trafiają na koniec, a istniejące wiadomości opuszczają początek głównej kolejki.
Taka synchronizacja odbywa się automatycznie lub ręcznie i jest zarządzana za pomocą polityki kolejek. Rozważmy przykład.
Mamy dwie lustrowane kolejki. Kolejka A synchronizuje się automatycznie, a Kolejka B ręcznie. W obu kolejkach jest po dziesięć wiadomości.

Rys. 8. Dwie kolejki z różnymi trybami synchronizacji
Teraz tracimy Brokera 3.

Rys. 9. Broker 3 upadł
Broker 3 wraca do działania. Klaster tworzy lustro dla każdej kolejki na nowym węźle i automatycznie synchronizuje nową Kolejkę A z masterem. Jednak lustro nowej Kolejki B pozostaje puste. W ten sposób mamy pełną nadmiarowość Kolejki A i tylko jedno lustro dla istniejących wiadomości Kolejki B.

Rys. 10. Nowe lustro Kolejki A otrzymuje wszystkie istniejące wiadomości, a nowe lustro Kolejki B — nie
Do obu kolejek dociera po dziesięć wiadomości. Następnie Broker 2 się zawiesza, a Kolejka A cofa się do najstarszego lustra, które znajduje się na Brokerze 1. W przypadku awarii nie dochodzi do utraty danych. W Kolejce B znajduje się dwadzieścia wiadomości w masterze i tylko dziesięć w lustrze, ponieważ ta kolejka nigdy nie replikowała początkowych dziesięciu wiadomości.

Rys. 11. Kolejka A cofa się do Brokera 1 bez utraty wiadomości.
Do obu kolejek dociera jeszcze po dziesięć wiadomości. Teraz pada Broker 1. Kolejka A bez problemu przełącza się na lustro bez utraty wiadomości. Jednak Kolejka B ma problemy. Na tym etapie możemy optymalizować albo dostępność, albo spójność.
Jeśli chcemy optymalizować dostępność, to politykę ha-promote-on-failure należy ustawić na always. To wartość domyślna, więc można po prostu nie określać polityki w ogóle. W takim przypadku zasadniczo dopuszczamy awarie w niesynchronizowanych lustrach. Doprowadzi to do utraty wiadomości, ale kolejka pozostaje dostępna do odczytu i zapisu.

Rys. 12. Kolejka A cofa się do Brokera 3 bez utraty wiadomości. Kolejka B cofa się do Brokera 3 z utratą dziesięciu wiadomości.
Możemy również ustawić ha-promote-on-failure na wartość when-synced. W tym przypadku zamiast cofać się do lustra kolejka będzie czekać, aż Broker 1 z danymi wróci do trybu operacyjnego. Po jego powrocie główna kolejka znowu znajduje się na Brokerze 1 bez utraty danych. Dostępność jest poświęcana bezpieczeństwu danych. Ale to ryzykowny tryb, który może prowadzić nawet do całkowitej utraty danych, co omówimy wkrótce.

Rys. 13. Kolejka B pozostaje niedostępna po utracie Brokera 1.
Możesz zadać pytanie: „Może lepiej nigdy nie używać automatycznej synchronizacji?”. Odpowiedź brzmi, że synchronizacja jest operacją blokującą. Podczas synchronizacji główna kolejka nie może wykonywać żadnych operacji odczytu lub zapisu!
Rozważmy przykład. Obecnie mamy bardzo duże kolejki. Jak mogą urosnąć do takiego rozmiaru? Z kilku powodów:
- Kolejki nie są aktywnie wykorzystywane.
- To szybkie kolejki, a obecnie konsumenci działają wolno.
- To szybkie kolejki, doszło do awarii i konsumenci doganiają.

Rys. 14. Dwie duże kolejki z różnymi trybami synchronizacji
Teraz pada Broker 3.

Rys. 15. Broker 3 pada, zostawiając po jednym masterze i lustrze w każdej kolejce
Broker 3 wraca do działania, a nowe lustra są tworzone. Główna Kolejka A zaczyna replikować istniejące wiadomości na nowe lustro, a w tym czasie Kolejka jest niedostępna. Replikacja danych zajmuje dwie godziny, co prowadzi do dwóch godzin przestoju dla tej Kolejki!
Jednak Kolejka B pozostaje dostępna przez cały ten czas. Poświęciła część redundancji na rzecz dostępności.

Rys. 16. Kolejka pozostaje niedostępna podczas synchronizacji
Po dwóch godzinach Kolejka A również staje się dostępna i może znowu rozpocząć operacje odczytu i zapisu.
Aktualizacje
Tego rodzaju blokujące zachowanie podczas synchronizacji utrudnia aktualizację klastrów z bardzo dużymi kolejkami. W pewnym momencie węzeł z masterem musi być ponownie uruchomiony, co oznacza przejście na lustro lub odłączenie kolejki podczas aktualizacji serwera. Jeśli wybierzemy przejście, stracimy wiadomości, jeśli lustra nie są zsynchronizowane. Domyślnie w czasie wyłączenia brokera przejście na niesynchronizowane lustro nie jest realizowane. Oznacza to, że gdy broker powraca, nie tracimy żadnych wiadomości, jedyną szkodą był jedynie przestój kolejki. Zasady zachowania podczas wyłączania brokera określa polityka ha-promote-on-shutdown. Można ustawić jedną z dwóch wartości:
always= włączone przejście na niesynchronizowane lustrawhen-synced= przejście tylko na zsynchronizowane lustro, w przeciwnym razie kolejka staje się niedostępna do odczytu i zapisu. Kolejka wraca do działania, gdy broker wróci
Tak czy inaczej, z dużymi kolejkami trzeba wybierać między utratą danych a niedostępnością.
Gdy dostępność zwiększa bezpieczeństwo danych
Zanim podejmiemy decyzję, należy wziąć pod uwagę jeszcze jedno utrudnienie. Chociaż automatyczna synchronizacja jest lepsza dla redundancji, jak wpływa na bezpieczeństwo danych? Oczywiście, dzięki lepszej redundancji RabbitMQ z mniejszym prawdopodobieństwem straci istniejące wiadomości, ale co z nowymi wiadomościami od wydawców?
Należy wziąć pod uwagę następujące kwestie:
- Czy wydawca może po prostu zwrócić błąd, a wyższa usługa lub użytkownik spróbują ponownie później?
- Czy wydawca może zapisać wiadomość lokalnie lub w bazie danych, aby spróbować ponownie później?
Jeśli wydawca może jedynie odrzucić wiadomość, to poprawa dostępności zwiększa również bezpieczeństwo danych.
W związku z tym należy szukać równowagi, a decyzja zależy od konkretnej sytuacji.
Problemy z ha-promote-on-failure=when-synced
Idea ha-promote-on-failure= when-synced polegają na tym, że uniemożliwiamy przełączenie na niesynchronizowane lustro, unikając tym samym utraty danych. Kolejka pozostaje niedostępna do odczytu lub zapisu. Zamiast tego staramy się przywrócić upadły broker z nienaruszonymi danymi, aby mógł wznowić pracę jako mistrz bez utraty danych.
Jednak (i to jest duże ale) jeśli broker stracił swoje dane, mamy duży problem: kolejka jest stracona! Wszystkie dane zniknęły! Nawet jeśli masz lustra, które w większości doganiają główną kolejkę, te lustra również są odrzucane.
Aby ponownie dodać węzeł o tej samej nazwie, mówimy klastrowi, aby zapomniał o utraconym węźle (poleceniem rabbitmqctl forget_cluster_node) i uruchamiamy nowego brokera o tej samej nazwie hosta. Dopóki klaster pamięta utracony węzeł, pamięta starą kolejkę i niesynchronizowane lustra. Gdy klasterowi każe się zapomnieć o utraconym węźle, ta kolejka również zostaje zapomniana. Teraz trzeba ją ponownie ogłosić. Straciliśmy wszystkie dane, chociaż mieliśmy lustra z częściowym zestawem danych. Lepiej byłoby przełączyć się na niesynchronizowane lustro!
Dlatego ręczna synchronizacja (i nie wykonywanie synchronizacji) w połączeniu z ha-promote-on-failure=when-synced, moim zdaniem, jest dość ryzykowna. Dokumenty mówią, że taka opcja istnieje dla bezpieczeństwa danych, ale to podwójnie ostrym nożem.
Przebalansowanie mistrzów
Jak obiecano, wracamy do problemu gromadzenia wszystkich mistrzów na jednym lub kilku węzłach. Może to się zdarzyć nawet w wyniku 'przechodniego' (rolling) aktualizowania klastra. W klastrze z trzema węzłami wszystkie główne kolejki zgromadzą się na jednym lub dwóch węzłach.
Przebalansowanie mistrzów może okazać się problematyczne z dwóch powodów:
- Brak dobrych narzędzi do wykonania przebalansowania
- Synchronizacja kolejek
Aby przeprowadzić rebalance, istnieje zewnętrzny , który nie jest oficjalnie wspierany. Na temat zewnętrznych pluginów w dokumentacji RabbitMQ : «Plugin zapewnia dodatkowe narzędzia konfiguracyjne i raportowe, ale nie jest wspierany i nie został zweryfikowany przez zespół RabbitMQ. Korzystasz z niego na własne ryzyko».
Istnieje jeszcze jeden trik, aby przenieść główną kolejkę za pomocą polityk HA. W dokumentacji wspomniane jest na ten temat. Działa to w następujący sposób:
- Usuwa wszystkie lustra za pomocą tymczasowej polityki o wyższym priorytecie niż istniejąca polityka HA.
- Zmienia tymczasową politykę HA, aby używała trybu „węzły” z określeniem węzła, na który ma zostać przeniesiona główna kolejka.
- Synchronizuje kolejkę dla wymuszonej migracji.
- Po zakończeniu migracji usuwa tymczasową politykę. Wchodzi w życie pierwotna polityka HA i tworzy odpowiednią liczbę luster.
Wadą tego podejścia jest to, że może nie zadziałać, jeśli masz duże kolejki lub rygorystyczne wymagania dotyczące nadmiarowości.
Teraz przyjrzyjmy się, jak klastry RabbitMQ działają w przypadku podziałów sieciowych.
Naruszenie spójności
Węzły rozproszonego systemu łączą się węzłami sieci, a połączenia sieciowe mogą i będą się przerywać. Częstotliwość przerwań zależy od lokalnej infrastruktury lub niezawodności wybranego chmury. W każdym razie rozproszone systemy muszą być w stanie sobie z nimi radzić. Ponownie mamy wybór między dostępnością a spójnością, a ponownie dobrą wiadomością jest to, że RabbitMQ zapewnia oba warianty (po prostu nie jednocześnie).
W RabbitMQ mamy dwie główne opcje:
- Pozwolić na logiczny podział (split-brain). Zapewnia to dostępność, ale może prowadzić do utraty danych.
- Zakazać logicznego podziału. Może prowadzić do krótkotrwałej utraty dostępności w zależności od sposobu połączenia klientów z klastrem. Może również prowadzić do całkowitej niedostępności w klastrze złożonym z dwóch węzłów.
Ale czym jest logiczny podział? To sytuacja, gdy klaster dzieli się na pół z powodu utraty połączeń sieciowych. Po każdej stronie lustra awansują do roli głównej, przez co ostatecznie na każdą kolejkę przypada kilka głównych.

Rys. 17. Główna kolejka i dwa lustra, każde na oddzielnym węźle. Następnie następuje awaria sieci, a jedno lustro się oddziela. Oddzielony węzeł widzi, że dwa inne odpadły i promuje swoje lustra do roli głównej. Teraz mamy dwie główne kolejki, z którymi można zarówno zapisywać, jak i odczytywać.
Jeżeli publikatorzy wysyłają dane do obu głównych, otrzymujemy dwa rozdzielające się kopie kolejki.
Różne tryby RabbitMQ zapewniają albo dostępność, albo spójność.
Tryb Ignoruj (domyślnie)
Ten tryb zapewnia dostępność. Po utracie spójności następuje logiczne podział. Po przywróceniu spójności administrator musi zdecydować, która strona ma być preferowana. Strona przegrana zostanie uruchomiona na nowo, a wszystkie nagromadzone dane z tej strony zostaną utracone.

Rys. 18. Trzech publikatorów jest połączonych z trzema brokerami. Wewnętrznie klaster kieruje wszystkie zapytania do głównej kolejki na Brokerze 2.
Teraz tracimy Brokera 3. Widział on, że inni brokerzy odpadli i promuje swoje lustro do roli głównej. Tak następuje logiczny podział.

Rys. 19. Logicznym podział (split-brain). Zapisy są kierowane do dwóch głównych kolejek, a dwie kopie się rozdzielają.
Spójność zostaje przywrócona, ale logiczny podział pozostaje. Administrator musi ręcznie wybrać przegraną stronę. W przedstawionym poniżej przypadku administrator restartuje Brokera 3. Wszystkie wiadomości, które nie zostały przez niego przekazane, zostaną utracone.

Rys. 20. Administrator wyłącza Brokera 3.

Rys. 21. Administrator uruchamia Brokera 3, a on dołącza do klastra, tracąc wszystkie wiadomości, które tam pozostały.
W czasie utraty spójności oraz po jej przywróceniu klaster i ta kolejka były dostępne do odczytu i zapisu.
Tryb Autoheal
Działa podobnie jak tryb Ignoruj, z wyjątkiem tego, że sam klaster automatycznie wybiera przegraną stronę po podziale i przywróceniu spójności. Przegrana strona wraca do klastra pusta, a kolejka traci wszystkie wiadomości, które były wysyłane tylko na tę stronę.
Tryb Pauza Mniejszości
Jeśli nie chcemy dopuścić do logicznego podziału, naszym jedynym rozwiązaniem jest rezygnacja z odczytu i zapisu po mniejszej stronie po podziale klastra. Kiedy broker widzi, że znajduje się po mniejszej stronie, wstrzymuje działanie, zamykając wszystkie istniejące połączenia i odrzucając wszelkie nowe. Co sekundę sprawdza odzyskanie łączności. Gdy tylko łączność zostanie przywrócona, wznawia działanie i ponownie łączy się z klastrem.

Rys. 22. Trzech wydawców jest połączonych z trzema brokerami. Wewnątrz klaster kieruje wszystkie zapytania do głównej kolejki na Brokerze 2.
Następnie Brokerzy 1 i 2 odłączają się od Brokera 3. Zamiast podnieść swoje lustro do roli mistrza, Broker 3 wstrzymuje działanie i staje się niedostępny.

Rys. 23. Broker 3 wstrzymuje działanie, odłącza wszystkich klientów i odrzuca zapytania o połączenie.
Gdy tylko łączność zostanie przywrócona, wraca do klastra.
Zobaczmy inny przykład, w którym główna kolejka znajduje się na Brokerze 3.

Rys. 24. Główna kolejka na Brokerze 3.
Następnie następuje ta sama utrata łączności. Broker 3 przechodzi w stan pauzy, ponieważ znajduje się po mniejszej stronie. Po drugiej stronie węzły widzą, że Broker 3 odpadł, więc starsze lustro z Brokerów 1 i 2 podnosi się do roli mistrza.

Rys. 25. Przejście do Brokera 2 przy niedostępności Brokera 3.
Gdy łączność zostanie przywrócona, Broker 3 dołączy do klastra.

Rys. 26. Klastar wrócił do normalnego działania.
Ważne jest, aby zrozumieć, że uzyskujemy spójność, ale możemy również uzyskać dostępność, jeśli sukcesywnie przeniesiemy klientów na większą część podziału. W większości sytuacji osobiście wybrałbym tryb Pauza Mniejszość, ale to naprawdę zależy od konkretnego przypadku.
Aby zapewnić dostępność, ważne jest, aby upewnić się, że klienci pomyślnie łączą się z węzłem. Rozważmy nasze opcje.
Zapewnienie łączności klientów
Mamy kilka sposobów, jak po utracie połączenia skierować klientów do głównej części klastra lub działających węzłów (po awarii jednego węzła). Najpierw przypomnijmy sobie, że konkretna kolejka jest umieszczana na określonym węźle, ale routingu i polityki są replikowane na wszystkich węzłach. Klienci mogą łączyć się z dowolnym węzłem, a wewnętrzne routingu skieruje ich tam, gdzie trzeba. Jednak gdy węzeł jest zawieszony, odrzuca połączenia, dlatego klienci muszą połączyć się z innym węzłem. Jeśli węzeł jest niedostępny, nie ma on za wiele do zaoferowania.
Nasze opcje:
- Dostęp do klastra odbywa się za pomocą load balancera, który po prostu cyklicznie przegląda węzły, a klienci próbują się połączyć, aż do sukcesu. Jeśli węzeł jest niedziałający lub zawieszony, to próby połączenia z tym węzłem zakończą się niepowodzeniem, ale kolejne próby skierują się do innych serwerów (w trybie cyklicznym). To rozwiązanie jest odpowiednie w przypadku krótkotrwałej utraty połączenia lub awarii serwera, który szybko zostanie uruchomiony ponownie.
- Dostęp do klastra przez load balancer i usunięcie zawieszonych/niedostępnych węzłów z listy, jak tylko będą wykryte. Jeśli uda się to szybko zrobić i jeśli klienci będą w stanie spróbować połączenia ponownie, to uzyskamy stałą dostępność.
- Dać każdemu klientowi listę wszystkich węzłów, a klient przy połączeniu losowo wybiera jeden z nich. Jeżeli przy próbie połączenia otrzyma błąd, przechodzi do następnego węzła na liście, aż się połączy.
- Usunąć ruch od niedziałającego/zawieszonego węzła za pomocą DNS. Robi się to przy użyciu małego TTL.
Wnioski
Klastrowanie RabbitMQ ma swoje zalety i wady. Najpoważniejsze wady to:
- przy dołączeniu do klastra węzły odrzucają swoje dane;
- blokująca synchronizacja prowadzi do niedostępności kolejki.
Wszystkie trudne decyzje wynikają z tych dwóch cech architektury. Gdyby RabbitMQ mogło przechowywać dane przy ponownym nawiązywaniu połączenia w klastrze, synchronizacja byłaby szybsza. Gdyby miało możliwość nienaładowej synchronizacji, lepiej obsługiwałoby duże kolejki. Rozwiązanie tych dwóch problemów znacznie poprawiłoby wydajność RabbitMQ jako technologii wymiany wiadomości o wysokiej dostępności i odporności na błędy. Nie odważyłbym się polecać RabbitMQ z klasteryzacją w następujących sytuacjach:
- Niezawodna sieć.
- Niezawodne przechowywanie.
- Bardzo duże kolejki.
Jeśli chodzi o ustawienia wysokiej dostępności, rozważ takie:
ha-promote-on-failure=alwaysha-sync-mode=manualcluster_partition_handling=ignore(lubautoheal)- trwałe wiadomości
- upewnij się, że klienci łączą się z aktywnym węzłem, gdy jakiś węzeł przestaje działać
Dla spójności (bezpieczeństwa danych) rozważ następujące ustawienia:
- Potwierdzenia wydawcy i ręczne potwierdzenia po stronie konsumenta
ha-promote-on-failure=when-synced, jeśli wydawcy mogą spróbować ponownie później i jeśli masz bardzo niezawodne przechowywanie! W przeciwnym razie ustaw=always.ha-sync-mode=automatic(ale dla dużych nieaktywnych kolejek może być wymagany tryb ręczny; ponadto zastanów się, czy niedostępność nie doprowadzi do utraty wiadomości)- tryb Pauza Mniejszości
- trwałe wiadomości
Nie omówiliśmy jeszcze wszystkich zagadnień dotyczących odporności na błędy i wysokiej dostępności; na przykład, jak bezpiecznie przeprowadzać procedury administracyjne (takie jak aktualizacje bez przestojów). Należy również porozmawiać o federacji i wtyczce Shovel.
Jeśli coś jeszcze pominąłem, daj mi znać.
Zobacz także mój , w którym dokonuję rozbicia klastra RabbitMQ przy użyciu Dockera i Blockade, aby przetestować niektóre scenariusze utraty wiadomości opisane w tym artykule.
Poprzednie artykuły z serii:
Nr 1 —
Nr 2 —
Nr 3 —
Źródło: habr.com
