{"id":52525,"date":"2019-11-10T00:00:00","date_gmt":"2019-11-09T21:00:00","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/rabbitmq-protiv-kafka-otkazoustojchivost-i-vysokaya-dostupnost-v-klasterah"},"modified":"2020-02-18T14:00:16","modified_gmt":"2020-02-18T11:00:16","slug":"rabbitmq-protiv-kafka-otkazoustojchivost-i-vysokaya-dostupnost-v-klasterah","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/rabbitmq-protiv-kafka-otkazoustojchivost-i-vysokaya-dostupnost-v-klasterah","title":{"rendered":"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/d8e584d210a6a016a962ad6f956a2078.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOdporno\u015b\u0107 i wysoka dost\u0119pno\u015b\u0107 to du\u017ce tematy, dlatego po\u015bwi\u0119cimy RabbitMQ i Kafka osobne artyku\u0142y. Ten artyku\u0142 dotyczy RabbitMQ, a nast\u0119pny b\u0119dzie o Kafka w por\u00f3wnaniu do RabbitMQ. Artyku\u0142 jest d\u0142ugi, wi\u0119c rozsi\u0105d\u017a si\u0119 wygodnie.<\/p>\n<p>Zastanowimy si\u0119 nad strategiami odporno\u015bci, sp\u00f3jno\u015bci oraz wysokiej dost\u0119pno\u015bci (HA), a tak\u017ce kompromisami, kt\u00f3re trzeba zaakceptowa\u0107 w ka\u017cdej strategii. RabbitMQ mo\u017ce dzia\u0142a\u0107 w klastrze w\u0119z\u0142\u00f3w, przez co klasyfikuje si\u0119 jako system rozproszony. Gdy m\u00f3wimy o systemach rozproszonych, cz\u0119sto poruszamy tematy sp\u00f3jno\u015bci i dost\u0119pno\u015bci. <\/p>\n<p>Te poj\u0119cia opisuj\u0105, jak system zachowuje si\u0119 w przypadku awarii. Awaria po\u0142\u0105czenia sieciowego, awaria serwera, awaria dysku twardego, tymczasowa niedost\u0119pno\u015b\u0107 serwera z powodu zbierania \u015bmieci, utrata pakiet\u00f3w lub spowolnienie po\u0142\u0105czenia sieciowego. Wszystko to mo\u017ce prowadzi\u0107 do utraty danych lub konflikt\u00f3w. Okazuje si\u0119, \u017ce praktycznie niemo\u017cliwe jest utrzymanie systemu, kt\u00f3ry jednocze\u015bnie by\u0142by ca\u0142kowicie sp\u00f3jny (bez utraty danych, bez rozbie\u017cno\u015bci danych) oraz dost\u0119pny (akceptuj\u0105cego operacje odczytu i zapisu) we wszystkich scenariuszach awarii.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nZobaczymy, \u017ce sp\u00f3jno\u015b\u0107 i dost\u0119pno\u015b\u0107 znajduj\u0105 si\u0119 na przeciwnych ko\u0144cach spektrum, a Ty musisz wybra\u0107, w kt\u00f3r\u0105 stron\u0119 optymalizowa\u0107. Dobr\u0105 wiadomo\u015bci\u0105 jest to, \u017ce z RabbitMQ taki wyb\u00f3r jest mo\u017cliwy. Masz \u201enerdowskie\u201d d\u017awignie, aby przesun\u0105\u0107 r\u00f3wnowag\u0119 w stron\u0119 wi\u0119kszej sp\u00f3jno\u015bci lub wi\u0119kszej dost\u0119pno\u015bci.<\/p>\n<p>Szczeg\u00f3ln\u0105 uwag\u0119 zwr\u00f3cimy na to, jakie konfiguracje prowadz\u0105 do utraty danych z powodu potwierdzonych zapis\u00f3w. Istnieje \u0142a\u0144cuch odpowiedzialno\u015bci mi\u0119dzy publikuj\u0105cymi, brokerami i konsumentami. Po tym jak wiadomo\u015b\u0107 zostanie przekazana brokerowi, to jego obowi\u0105zek \u2014 nie zgubi\u0107 tej wiadomo\u015bci. Gdy broker potwierdza publikuj\u0105cemu otrzymanie wiadomo\u015bci, nie oczekujemy, \u017ce zostanie ona utracona. Jednak zobaczymy, \u017ce mo\u017ce si\u0119 to zdarzy\u0107 w zale\u017cno\u015bci od konfiguracji Twojego brokera i wydawcy.<\/p>\n<h1>Primitives of node resilience<\/h1>\n<p><\/p>\n<h3>Odporn\u0105 kolejki\/routing<\/h3>\n<p>\nW RabbitMQ istniej\u0105 dwa typy kolejek: trwa\u0142e (durable) i nietrwa\u0142e (non-durable). Wszystkie kolejki s\u0105 przechowywane w bazie danych Mnesia. Trwa\u0142e kolejki s\u0105 ponownie deklarowane podczas uruchamiania w\u0119z\u0142a, co oznacza, \u017ce przetrwaj\u0105 ponowne uruchomienie, awari\u0119 systemu lub serwera (o ile dane s\u0105 zachowane). Oznacza to, \u017ce dop\u00f3ki deklarujesz routowanie (exchange) i kolejk\u0119 jako trwa\u0142e, infrastruktura kolejek\/routingu powr\u00f3ci do trybu operacyjnego.<\/p>\n<p>Nietrwa\u0142e kolejki i routowanie s\u0105 usuwane podczas ponownego uruchomienia w\u0119z\u0142a.<\/p>\n<h3>Trwa\u0142e wiadomo\u015bci<\/h3>\n<p>\nTo, \u017ce kolejka jest trwa\u0142a, nie oznacza, \u017ce wszystkie jej wiadomo\u015bci przetrwaj\u0105 ponowne uruchomienie w\u0119z\u0142a. Zostan\u0105 odzyskane tylko wiadomo\u015bci, kt\u00f3re zosta\u0142y oznaczone przez publikatora jako <i>trwa\u0142e<\/i> (persistent). Trwa\u0142e wiadomo\u015bci rzeczywi\u015bcie zwi\u0119kszaj\u0105 obci\u0105\u017cenie brokera, ale je\u015bli utrata wiadomo\u015bci jest nieakceptowalna, to nie ma innej opcji.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/8b8e2de4ef82bf8497960657aac0ddb0.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 1. Macierz trwa\u0142o\u015bci<\/i><\/p>\n<h1>Klasteryzacja z lustrzanym odbiciem kolejek<\/h1>\n<p>\nAby przetrwa\u0107 utrat\u0119 brokera, potrzebujemy nadmiarowo\u015bci. Mo\u017cemy po\u0142\u0105czy\u0107 kilka w\u0119z\u0142\u00f3w RabbitMQ w klaster, a nast\u0119pnie doda\u0107 dodatkow\u0105 nadmiarowo\u015b\u0107 poprzez replikacj\u0119 kolejek pomi\u0119dzy wieloma w\u0119z\u0142ami. W ten spos\u00f3b, je\u015bli jeden w\u0119ze\u0142 zawiedzie, nie tracimy danych i pozostajemy dost\u0119pni. <\/p>\n<p>Lustrzane odbicie kolejki:<\/p>\n<ul>\n<li>jedna g\u0142\u00f3wna kolejka (master), kt\u00f3ra otrzymuje wszystkie polecenia zapisu i odczytu\n<\/li>\n<li>jedno lub wi\u0119cej luster, kt\u00f3re otrzymuj\u0105 wszystkie wiadomo\u015bci i metadane z g\u0142\u00f3wnej kolejki. Te lustra istniej\u0105 nie do skalowania, ale wy\u0142\u0105cznie dla nadmiarowo\u015bci.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/35f3d4bdc4f5e10da0e40eb91c5d8280.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 2. Lustrzane odbicie kolejki<\/i><\/p>\n<p>Lustrzane odbicie jest ustawiane odpowiedni\u0105 polityk\u0105. Mo\u017cna w niej wybra\u0107 wsp\u00f3\u0142czynnik replikacji oraz nawet w\u0119z\u0142y, na kt\u00f3rych powinna si\u0119 znajdowa\u0107 kolejka. Przyk\u0142ady:<\/p>\n<ul>\n<li><code>ha-mode: all<\/code>\n<\/li>\n<li><code>ha-mode: exactly, ha-params: 2<\/code> (jeden master i jedno lustro)\n<\/li>\n<li><code>ha-mode: nodes, ha-params: rabbit@node1, rabbit@node2<\/code><\/li>\n<\/ul>\n<p><\/p>\n<h1>Potwierdzenie dla publikatora<\/h1>\n<p>\nAby uzyska\u0107 sp\u00f3jn\u0105 rejestracj\u0119, wymagane s\u0105 potwierdzenia od wydawcy (Publisher Confirms). Bez nich istnieje ryzyko utraty wiadomo\u015bci. Potwierdzenie jest wysy\u0142ane do wydawcy po zapisaniu wiadomo\u015bci na dysku. RabbitMQ zapisuje wiadomo\u015bci na dysku nie w momencie odbioru, ale w regularnych odst\u0119pach, co kilka setek milisekund. Gdy kolejka jest replikowana, potwierdzenie jest wysy\u0142ane dopiero po tym, jak wszystkie repliki r\u00f3wnie\u017c zapisa\u0142y swoj\u0105 kopi\u0119 wiadomo\u015bci na dysku. Oznacza to, \u017ce u\u017cycie potwierdze\u0144 dodaje op\u00f3\u017anienie, ale je\u015bli bezpiecze\u0144stwo danych jest wa\u017cne, s\u0105 one niezb\u0119dne.<\/p>\n<h1>Odporna kolejka<\/h1>\n<p>\nGdy broker ko\u0144czy prac\u0119 lub ulega awarii, wszystkie g\u0142\u00f3wne kolejki (master) na tym w\u0119\u017ale przestaj\u0105 dzia\u0142a\u0107. Nast\u0119pnie klaster wybiera najstarsz\u0105 replik\u0119 ka\u017cdego mastera i promuje j\u0105 do nowego mastera.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/8d8227c00643f35e0dc752b6617e18fb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 3. Kilka replikowanych kolejek i ich polityki<\/i><\/p>\n<p>Broker 3 ulega awarii. Zauwa\u017c, \u017ce replikacja Kolejki C na Brokerze 2 jest promowana do mastera. Zauwa\u017c tak\u017ce, \u017ce dla Kolejki C utworzono now\u0105 replik\u0119 na Brokerze 1. RabbitMQ zawsze stara si\u0119 utrzyma\u0107 wska\u017anik replikacji okre\u015blony w twoich politykach.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/d08b3ada22e79686d448714f7bab009a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 4. Broker 3 ulega awarii, co powoduje awari\u0119 kolejki C<\/i> <\/p>\n<p>Ulega awarii kolejny Broker 1! Zosta\u0142 nam tylko jeden broker. Replika Kolejki B jest promowana do mastera.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/dcc1d542c40c441194fd07e51046c619.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 5<\/i><\/p>\n<p>Przywr\u00f3cili\u015bmy Brokera 1. Niezale\u017cnie od tego, jak pomy\u015blnie dane przetrwa\u0142y utrat\u0119 i przywr\u00f3cenie brokera, wszystkie replikowane wiadomo\u015bci kolejki s\u0105 odrzucane podczas ponownego uruchamiania. Warto to zaznaczy\u0107, gdy\u017c b\u0119d\u0105 tego konsekwencje. Wkr\u00f3tce przyjrzymy si\u0119 tym konsekwencjom. W zwi\u0105zku z tym Broker 1 zn\u00f3w jest cz\u0142onkiem klastra, a klaster stara si\u0119 przestrzega\u0107 polityk i dlatego tworzy replik\u0119 na Brokerze 1.<\/p>\n<p>W tym przypadku utrata Brokera 1 by\u0142a ca\u0142kowita, jak i danych, dlatego niezreplikowana Kolejka B zosta\u0142a ca\u0142kowicie utracona.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/7ac7ab65ea6dff9c5a49372c04c83a37.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 6. Broker 1 wraca do dzia\u0142ania<\/i><\/p>\n<p>Broker 3 wr\u00f3ci\u0142 do dzia\u0142ania, wi\u0119c kolejki A i B odzyskuj\u0105 utworzone na nim lustra, aby zaspokoi\u0107 swoje polityki HA. Ale teraz wszystkie g\u0142\u00f3wne kolejki s\u0105 na jednym w\u0119\u017ale! To nie jest idealne, lepiej by\u0142oby r\u00f3wnomierne roz\u0142o\u017cenie mi\u0119dzy w\u0119z\u0142ami. Niestety, nie ma tutaj szczeg\u00f3lnych opcji do przewa\u017cenia master\u00f3w. Wr\u00f3cimy do tego problemu p\u00f3\u017aniej, poniewa\u017c najpierw musimy rozwa\u017cy\u0107 synchronizacj\u0119 kolejki. <\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/a54e53bb01e2f830a54f88acaa668984.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 7. Broker 3 wraca do dzia\u0142ania. Wszystkie g\u0142\u00f3wne kolejki na jednym w\u0119\u017ale!<\/i><\/p>\n<p>W ten spos\u00f3b powiniene\u015b ju\u017c mie\u0107 wyobra\u017cenie, jak lustra zapewniaj\u0105 nadmiarowo\u015b\u0107 i odporno\u015b\u0107 na awarie. Gwarantuje to dost\u0119pno\u015b\u0107 w przypadku awarii jednego w\u0119z\u0142a i chroni przed utrat\u0105 danych. Ale nie sko\u0144czyli\u015bmy jeszcze, poniewa\u017c w rzeczywisto\u015bci wszystko jest znacznie bardziej skomplikowane.<\/p>\n<h1>Synchronizacja<\/h1>\n<p>\nPodczas tworzenia nowego lustra wszystkie nowe wiadomo\u015bci zawsze b\u0119d\u0105 replikowane na to lustro i wszelkie inne. Je\u015bli chodzi o istniej\u0105ce dane w g\u0142\u00f3wnej kolejce, mo\u017cemy je zreplikowa\u0107 w nowe lustro, kt\u00f3re staje si\u0119 pe\u0142n\u0105 kopi\u0105 mastera. Mo\u017cemy r\u00f3wnie\u017c nie replikowa\u0107 istniej\u0105cych wiadomo\u015bci i pozwoli\u0107 g\u0142\u00f3wnej kolejce i nowemu lustrze synchronizowa\u0107 si\u0119 w czasie, w kt\u00f3rym nowe wiadomo\u015bci trafiaj\u0105 na koniec, a istniej\u0105ce wiadomo\u015bci opuszczaj\u0105 pocz\u0105tek g\u0142\u00f3wnej kolejki.<\/p>\n<p>Taka synchronizacja odbywa si\u0119 automatycznie lub r\u0119cznie i jest zarz\u0105dzana za pomoc\u0105 polityki kolejek. Rozwa\u017cmy przyk\u0142ad.<\/p>\n<p>Mamy dwie lustrowane kolejki. Kolejka A synchronizuje si\u0119 automatycznie, a Kolejka B r\u0119cznie. W obu kolejkach jest po dziesi\u0119\u0107 wiadomo\u015bci.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/304f2a475fdd0cb6d3069140c667a0a7.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 8. Dwie kolejki z r\u00f3\u017cnymi trybami synchronizacji<\/i><\/p>\n<p>Teraz tracimy Brokera 3.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/49114966f440c407488bb467ff6dbf93.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 9. Broker 3 upad\u0142<\/i><\/p>\n<p>Broker 3 wraca do dzia\u0142ania. Klaster tworzy lustro dla ka\u017cdej kolejki na nowym w\u0119\u017ale i automatycznie synchronizuje now\u0105 Kolejk\u0119 A z masterem. Jednak lustro nowej Kolejki B pozostaje puste. W ten spos\u00f3b mamy pe\u0142n\u0105 nadmiarowo\u015b\u0107 Kolejki A i tylko jedno lustro dla istniej\u0105cych wiadomo\u015bci Kolejki B.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/adffc1f5eff8806edbd83772f338d67f.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 10. Nowe lustro Kolejki A otrzymuje wszystkie istniej\u0105ce wiadomo\u015bci, a nowe lustro Kolejki B \u2014 nie<\/i><\/p>\n<p>Do obu kolejek dociera po dziesi\u0119\u0107 wiadomo\u015bci. Nast\u0119pnie Broker 2 si\u0119 zawiesza, a Kolejka A cofa si\u0119 do najstarszego lustra, kt\u00f3re znajduje si\u0119 na Brokerze 1. W przypadku awarii nie dochodzi do utraty danych. W Kolejce B znajduje si\u0119 dwadzie\u015bcia wiadomo\u015bci w masterze i tylko dziesi\u0119\u0107 w lustrze, poniewa\u017c ta kolejka nigdy nie replikowa\u0142a pocz\u0105tkowych dziesi\u0119ciu wiadomo\u015bci.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/c6cfef4bced5627bf243c2f020a42646.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 11. Kolejka A cofa si\u0119 do Brokera 1 bez utraty wiadomo\u015bci.<\/i><\/p>\n<p>Do obu kolejek dociera jeszcze po dziesi\u0119\u0107 wiadomo\u015bci. Teraz pada Broker 1. Kolejka A bez problemu prze\u0142\u0105cza si\u0119 na lustro bez utraty wiadomo\u015bci. Jednak Kolejka B ma problemy. Na tym etapie mo\u017cemy optymalizowa\u0107 albo dost\u0119pno\u015b\u0107, albo sp\u00f3jno\u015b\u0107. <\/p>\n<p>Je\u015bli chcemy optymalizowa\u0107 dost\u0119pno\u015b\u0107, to polityk\u0119 <b><i>ha-promote-on-failure<\/i><\/b> nale\u017cy ustawi\u0107 na <b><i>always<\/i><\/b>. To warto\u015b\u0107 domy\u015blna, wi\u0119c mo\u017cna po prostu nie okre\u015bla\u0107 polityki w og\u00f3le. W takim przypadku zasadniczo dopuszczamy awarie w niesynchronizowanych lustrach. Doprowadzi to do utraty wiadomo\u015bci, ale kolejka pozostaje dost\u0119pna do odczytu i zapisu.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/cd704cf4397184c212e1576b986abbaa.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 12. Kolejka A cofa si\u0119 do Brokera 3 bez utraty wiadomo\u015bci. Kolejka B cofa si\u0119 do Brokera 3 z utrat\u0105 dziesi\u0119ciu wiadomo\u015bci.<\/i><\/p>\n<p>Mo\u017cemy r\u00f3wnie\u017c ustawi\u0107 <code>ha-promote-on-failure<\/code> na warto\u015b\u0107 <code>when-synced<\/code>. W tym przypadku zamiast cofa\u0107 si\u0119 do lustra kolejka b\u0119dzie czeka\u0107, a\u017c Broker 1 z danymi wr\u00f3ci do trybu operacyjnego. Po jego powrocie g\u0142\u00f3wna kolejka znowu znajduje si\u0119 na Brokerze 1 bez utraty danych. Dost\u0119pno\u015b\u0107 jest po\u015bwi\u0119cana bezpiecze\u0144stwu danych. Ale to ryzykowny tryb, kt\u00f3ry mo\u017ce prowadzi\u0107 nawet do ca\u0142kowitej utraty danych, co om\u00f3wimy wkr\u00f3tce.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/03fed936fe75220e22f68432ca81b654.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 13. Kolejka B pozostaje niedost\u0119pna po utracie Brokera 1.<\/i><\/p>\n<p>Mo\u017cesz zada\u0107 pytanie: \u201eMo\u017ce lepiej nigdy nie u\u017cywa\u0107 automatycznej synchronizacji?\u201d. Odpowied\u017a brzmi, \u017ce synchronizacja jest operacj\u0105 blokuj\u0105c\u0105. Podczas synchronizacji g\u0142\u00f3wna kolejka nie mo\u017ce wykonywa\u0107 \u017cadnych operacji odczytu lub zapisu!<\/p>\n<p>Rozwa\u017cmy przyk\u0142ad. Obecnie mamy bardzo du\u017ce kolejki. Jak mog\u0105 urosn\u0105\u0107 do takiego rozmiaru? Z kilku powod\u00f3w:<\/p>\n<ul>\n<li>Kolejki nie s\u0105 aktywnie wykorzystywane.\n<\/li>\n<li>To szybkie kolejki, a obecnie konsumenci dzia\u0142aj\u0105 wolno. \n<\/li>\n<li>To szybkie kolejki, dosz\u0142o do awarii i konsumenci doganiaj\u0105.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/dd4b0fd70cbbc0b7defc06536157770d.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 14. Dwie du\u017ce kolejki z r\u00f3\u017cnymi trybami synchronizacji<\/i><\/p>\n<p>Teraz pada Broker 3.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/8c31cfdf485ab4c9dfe13e86a151df4f.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 15. Broker 3 pada, zostawiaj\u0105c po jednym masterze i lustrze w ka\u017cdej kolejce<\/i><\/p>\n<p>Broker 3 wraca do dzia\u0142ania, a nowe lustra s\u0105 tworzone. G\u0142\u00f3wna Kolejka A zaczyna replikowa\u0107 istniej\u0105ce wiadomo\u015bci na nowe lustro, a w tym czasie Kolejka jest niedost\u0119pna. Replikacja danych zajmuje dwie godziny, co prowadzi do dw\u00f3ch godzin przestoju dla tej Kolejki!<\/p>\n<p>Jednak Kolejka B pozostaje dost\u0119pna przez ca\u0142y ten czas. Po\u015bwi\u0119ci\u0142a cz\u0119\u015b\u0107 redundancji na rzecz dost\u0119pno\u015bci.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/28e641300a1c3f4d70e29a16db51521e.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 16. Kolejka pozostaje niedost\u0119pna podczas synchronizacji<\/i><\/p>\n<p>Po dw\u00f3ch godzinach Kolejka A r\u00f3wnie\u017c staje si\u0119 dost\u0119pna i mo\u017ce znowu rozpocz\u0105\u0107 operacje odczytu i zapisu.<\/p>\n<h3>Aktualizacje<\/h3>\n<p>\nTego rodzaju blokuj\u0105ce zachowanie podczas synchronizacji utrudnia aktualizacj\u0119 klastr\u00f3w z bardzo du\u017cymi kolejkami. W pewnym momencie w\u0119ze\u0142 z masterem musi by\u0107 ponownie uruchomiony, co oznacza przej\u015bcie na lustro lub od\u0142\u0105czenie kolejki podczas aktualizacji serwera. Je\u015bli wybierzemy przej\u015bcie, stracimy wiadomo\u015bci, je\u015bli lustra nie s\u0105 zsynchronizowane. Domy\u015blnie w czasie wy\u0142\u0105czenia brokera przej\u015bcie na niesynchronizowane lustro nie jest realizowane. Oznacza to, \u017ce gdy broker powraca, nie tracimy \u017cadnych wiadomo\u015bci, jedyn\u0105 szkod\u0105 by\u0142 jedynie przest\u00f3j kolejki. Zasady zachowania podczas wy\u0142\u0105czania brokera okre\u015bla polityka <code>ha-promote-on-shutdown<\/code>. Mo\u017cna ustawi\u0107 jedn\u0105 z dw\u00f3ch warto\u015bci:<\/p>\n<ul>\n<li><code>always<\/code>= w\u0142\u0105czone przej\u015bcie na niesynchronizowane lustra\n<\/li>\n<li><code>when-synced<\/code>= przej\u015bcie tylko na zsynchronizowane lustro, w przeciwnym razie kolejka staje si\u0119 niedost\u0119pna do odczytu i zapisu. Kolejka wraca do dzia\u0142ania, gdy broker wr\u00f3ci<\/li>\n<\/ul>\n<p>\nTak czy inaczej, z du\u017cymi kolejkami trzeba wybiera\u0107 mi\u0119dzy utrat\u0105 danych a niedost\u0119pno\u015bci\u0105.<\/p>\n<h3>Gdy dost\u0119pno\u015b\u0107 zwi\u0119ksza bezpiecze\u0144stwo danych<\/h3>\n<p>\nZanim podejmiemy decyzj\u0119, nale\u017cy wzi\u0105\u0107 pod uwag\u0119 jeszcze jedno utrudnienie. Chocia\u017c automatyczna synchronizacja jest lepsza dla redundancji, jak wp\u0142ywa na bezpiecze\u0144stwo danych? Oczywi\u015bcie, dzi\u0119ki lepszej redundancji RabbitMQ z mniejszym prawdopodobie\u0144stwem straci istniej\u0105ce wiadomo\u015bci, ale co z nowymi wiadomo\u015bciami od wydawc\u00f3w?<\/p>\n<p>Nale\u017cy wzi\u0105\u0107 pod uwag\u0119 nast\u0119puj\u0105ce kwestie:<\/p>\n<ul>\n<li>Czy wydawca mo\u017ce po prostu zwr\u00f3ci\u0107 b\u0142\u0105d, a wy\u017csza us\u0142uga lub u\u017cytkownik spr\u00f3buj\u0105 ponownie p\u00f3\u017aniej?\n<\/li>\n<li>Czy wydawca mo\u017ce zapisa\u0107 wiadomo\u015b\u0107 lokalnie lub w bazie danych, aby spr\u00f3bowa\u0107 ponownie p\u00f3\u017aniej?<\/li>\n<\/ul>\n<p>\nJe\u015bli wydawca mo\u017ce jedynie odrzuci\u0107 wiadomo\u015b\u0107, to poprawa dost\u0119pno\u015bci zwi\u0119ksza r\u00f3wnie\u017c bezpiecze\u0144stwo danych.<\/p>\n<p>W zwi\u0105zku z tym nale\u017cy szuka\u0107 r\u00f3wnowagi, a decyzja zale\u017cy od konkretnej sytuacji.<\/p>\n<h1>Problemy z ha-promote-on-failure=when-synced<\/h1>\n<p>\nIdea <i><b>ha-promote-on-failure<\/b><\/i>= <i><b>when-synced<\/b><\/i> polegaj\u0105 na tym, \u017ce uniemo\u017cliwiamy prze\u0142\u0105czenie na niesynchronizowane lustro, unikaj\u0105c tym samym utraty danych. Kolejka pozostaje niedost\u0119pna do odczytu lub zapisu. Zamiast tego staramy si\u0119 przywr\u00f3ci\u0107 upad\u0142y broker z nienaruszonymi danymi, aby m\u00f3g\u0142 wznowi\u0107 prac\u0119 jako mistrz bez utraty danych. <\/p>\n<p>Jednak (i to jest du\u017ce ale) je\u015bli broker straci\u0142 swoje dane, mamy du\u017cy problem: kolejka jest stracona! Wszystkie dane znikn\u0119\u0142y! Nawet je\u015bli masz lustra, kt\u00f3re w wi\u0119kszo\u015bci doganiaj\u0105 g\u0142\u00f3wn\u0105 kolejk\u0119, te lustra r\u00f3wnie\u017c s\u0105 odrzucane.<\/p>\n<p>Aby ponownie doda\u0107 w\u0119ze\u0142 o tej samej nazwie, m\u00f3wimy klastrowi, aby zapomnia\u0142 o utraconym w\u0119\u017ale (poleceniem <i>rabbitmqctl forget_cluster_node<\/i>) i uruchamiamy nowego brokera o tej samej nazwie hosta. Dop\u00f3ki klaster pami\u0119ta utracony w\u0119ze\u0142, pami\u0119ta star\u0105 kolejk\u0119 i niesynchronizowane lustra. Gdy klasterowi ka\u017ce si\u0119 zapomnie\u0107 o utraconym w\u0119\u017ale, ta kolejka r\u00f3wnie\u017c zostaje zapomniana. Teraz trzeba j\u0105 ponownie og\u0142osi\u0107. Stracili\u015bmy wszystkie dane, chocia\u017c mieli\u015bmy lustra z cz\u0119\u015bciowym zestawem danych. Lepiej by\u0142oby prze\u0142\u0105czy\u0107 si\u0119 na niesynchronizowane lustro!<\/p>\n<p>Dlatego r\u0119czna synchronizacja (i nie wykonywanie synchronizacji) w po\u0142\u0105czeniu z <code>ha-promote-on-failure=when-synced<\/code>, moim zdaniem, jest do\u015b\u0107 ryzykowna. Dokumenty m\u00f3wi\u0105, \u017ce taka opcja istnieje dla bezpiecze\u0144stwa danych, ale to podw\u00f3jnie ostrym no\u017cem.<\/p>\n<h1>Przebalansowanie mistrz\u00f3w<\/h1>\n<p>\nJak obiecano, wracamy do problemu gromadzenia wszystkich mistrz\u00f3w na jednym lub kilku w\u0119z\u0142ach. Mo\u017ce to si\u0119 zdarzy\u0107 nawet w wyniku 'przechodniego' (rolling) aktualizowania klastra. W klastrze z trzema w\u0119z\u0142ami wszystkie g\u0142\u00f3wne kolejki zgromadz\u0105 si\u0119 na jednym lub dw\u00f3ch w\u0119z\u0142ach.<\/p>\n<p>Przebalansowanie mistrz\u00f3w mo\u017ce okaza\u0107 si\u0119 problematyczne z dw\u00f3ch powod\u00f3w:<\/p>\n<ul>\n<li>Brak dobrych narz\u0119dzi do wykonania przebalansowania<\/li>\n<li>Synchronizacja kolejek<\/li>\n<\/ul>\n<p>\nAby przeprowadzi\u0107 rebalance, istnieje zewn\u0119trzny <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/Ayanda-D\/rabbitmq-queue-master-balancer\">plugin<\/a><\/noindex>, kt\u00f3ry nie jest oficjalnie wspierany. Na temat zewn\u0119trznych plugin\u00f3w w dokumentacji RabbitMQ <noindex><a rel=\"nofollow\" href=\"https:\/\/www.rabbitmq.com\/upgrade.html\">stwierdzono<\/a><\/noindex>: \u00abPlugin zapewnia dodatkowe narz\u0119dzia konfiguracyjne i raportowe, ale nie jest wspierany i nie zosta\u0142 zweryfikowany przez zesp\u00f3\u0142 RabbitMQ. Korzystasz z niego na w\u0142asne ryzyko\u00bb.<\/p>\n<p>Istnieje jeszcze jeden trik, aby przenie\u015b\u0107 g\u0142\u00f3wn\u0105 kolejk\u0119 za pomoc\u0105 polityk HA. W dokumentacji wspomniane jest <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/rabbitmq\/support-tools\/blob\/master\/scripts\/rebalance-queue-masters\">skrypcie<\/a><\/noindex> na ten temat. Dzia\u0142a to w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<ul>\n<li>Usuwa wszystkie lustra za pomoc\u0105 tymczasowej polityki o wy\u017cszym priorytecie ni\u017c istniej\u0105ca polityka HA.\n<\/li>\n<li>Zmienia tymczasow\u0105 polityk\u0119 HA, aby u\u017cywa\u0142a trybu \u201ew\u0119z\u0142y\u201d z okre\u015bleniem w\u0119z\u0142a, na kt\u00f3ry ma zosta\u0107 przeniesiona g\u0142\u00f3wna kolejka.\n<\/li>\n<li>Synchronizuje kolejk\u0119 dla wymuszonej migracji.\n<\/li>\n<li>Po zako\u0144czeniu migracji usuwa tymczasow\u0105 polityk\u0119. Wchodzi w \u017cycie pierwotna polityka HA i tworzy odpowiedni\u0105 liczb\u0119 luster.<\/li>\n<\/ul>\n<p>\nWad\u0105 tego podej\u015bcia jest to, \u017ce mo\u017ce nie zadzia\u0142a\u0107, je\u015bli masz du\u017ce kolejki lub rygorystyczne wymagania dotycz\u0105ce nadmiarowo\u015bci.<\/p>\n<p>Teraz przyjrzyjmy si\u0119, jak klastry RabbitMQ dzia\u0142aj\u0105 w przypadku podzia\u0142\u00f3w sieciowych.<\/p>\n<h1>Naruszenie sp\u00f3jno\u015bci<\/h1>\n<p>\nW\u0119z\u0142y rozproszonego systemu \u0142\u0105cz\u0105 si\u0119 w\u0119z\u0142ami sieci, a po\u0142\u0105czenia sieciowe mog\u0105 i b\u0119d\u0105 si\u0119 przerywa\u0107. Cz\u0119stotliwo\u015b\u0107 przerwa\u0144 zale\u017cy od lokalnej infrastruktury lub niezawodno\u015bci wybranego chmury. W ka\u017cdym razie rozproszone systemy musz\u0105 by\u0107 w stanie sobie z nimi radzi\u0107. Ponownie mamy wyb\u00f3r mi\u0119dzy dost\u0119pno\u015bci\u0105 a sp\u00f3jno\u015bci\u0105, a ponownie dobr\u0105 wiadomo\u015bci\u0105 jest to, \u017ce RabbitMQ zapewnia oba warianty (po prostu nie jednocze\u015bnie).<\/p>\n<p>W RabbitMQ mamy dwie g\u0142\u00f3wne opcje:<\/p>\n<ul>\n<li>Pozwoli\u0107 na logiczny podzia\u0142 (split-brain). Zapewnia to dost\u0119pno\u015b\u0107, ale mo\u017ce prowadzi\u0107 do utraty danych.\n<\/li>\n<li>Zakaza\u0107 logicznego podzia\u0142u. Mo\u017ce prowadzi\u0107 do kr\u00f3tkotrwa\u0142ej utraty dost\u0119pno\u015bci w zale\u017cno\u015bci od sposobu po\u0142\u0105czenia klient\u00f3w z klastrem. Mo\u017ce r\u00f3wnie\u017c prowadzi\u0107 do ca\u0142kowitej niedost\u0119pno\u015bci w klastrze z\u0142o\u017conym z dw\u00f3ch w\u0119z\u0142\u00f3w.<\/li>\n<\/ul>\n<p>\nAle czym jest logiczny podzia\u0142? To sytuacja, gdy klaster dzieli si\u0119 na p\u00f3\u0142 z powodu utraty po\u0142\u0105cze\u0144 sieciowych. Po ka\u017cdej stronie lustra awansuj\u0105 do roli g\u0142\u00f3wnej, przez co ostatecznie na ka\u017cd\u0105 kolejk\u0119 przypada kilka g\u0142\u00f3wnych.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/d880a935df450fb994edfed0715e026e.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 17. G\u0142\u00f3wna kolejka i dwa lustra, ka\u017cde na oddzielnym w\u0119\u017ale. Nast\u0119pnie nast\u0119puje awaria sieci, a jedno lustro si\u0119 oddziela. Oddzielony w\u0119ze\u0142 widzi, \u017ce dwa inne odpad\u0142y i promuje swoje lustra do roli g\u0142\u00f3wnej. Teraz mamy dwie g\u0142\u00f3wne kolejki, z kt\u00f3rymi mo\u017cna zar\u00f3wno zapisywa\u0107, jak i odczytywa\u0107.<\/i> <\/p>\n<p>Je\u017celi publikatorzy wysy\u0142aj\u0105 dane do obu g\u0142\u00f3wnych, otrzymujemy dwa rozdzielaj\u0105ce si\u0119 kopie kolejki.<\/p>\n<p>R\u00f3\u017cne tryby RabbitMQ zapewniaj\u0105 albo dost\u0119pno\u015b\u0107, albo sp\u00f3jno\u015b\u0107.<\/p>\n<h3>Tryb Ignoruj (domy\u015blnie)<\/h3>\n<p>\nTen tryb zapewnia dost\u0119pno\u015b\u0107. Po utracie sp\u00f3jno\u015bci nast\u0119puje logiczne podzia\u0142. Po przywr\u00f3ceniu sp\u00f3jno\u015bci administrator musi zdecydowa\u0107, kt\u00f3ra strona ma by\u0107 preferowana. Strona przegrana zostanie uruchomiona na nowo, a wszystkie nagromadzone dane z tej strony zostan\u0105 utracone.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/4d82c13b5ef21837ba819f5c246a1e41.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 18. Trzech publikator\u00f3w jest po\u0142\u0105czonych z trzema brokerami. Wewn\u0119trznie klaster kieruje wszystkie zapytania do g\u0142\u00f3wnej kolejki na Brokerze 2.<\/i><\/p>\n<p>Teraz tracimy Brokera 3. Widzia\u0142 on, \u017ce inni brokerzy odpadli i promuje swoje lustro do roli g\u0142\u00f3wnej. Tak nast\u0119puje logiczny podzia\u0142.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/c7e01d51b24023bacdd93cfad95c9eab.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 19. Logicznym podzia\u0142 (split-brain). Zapisy s\u0105 kierowane do dw\u00f3ch g\u0142\u00f3wnych kolejek, a dwie kopie si\u0119 rozdzielaj\u0105.<\/i><\/p>\n<p>Sp\u00f3jno\u015b\u0107 zostaje przywr\u00f3cona, ale logiczny podzia\u0142 pozostaje. Administrator musi r\u0119cznie wybra\u0107 przegran\u0105 stron\u0119. W przedstawionym poni\u017cej przypadku administrator restartuje Brokera 3. Wszystkie wiadomo\u015bci, kt\u00f3re nie zosta\u0142y przez niego przekazane, zostan\u0105 utracone.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/51c3ee15024d3c83ba625e3ff9cdeb90.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 20. Administrator wy\u0142\u0105cza Brokera 3.<\/i><\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/0bac8e33de25c0c5381d336d26464858.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 21. Administrator uruchamia Brokera 3, a on do\u0142\u0105cza do klastra, trac\u0105c wszystkie wiadomo\u015bci, kt\u00f3re tam pozosta\u0142y.<\/i><\/p>\n<p>W czasie utraty sp\u00f3jno\u015bci oraz po jej przywr\u00f3ceniu klaster i ta kolejka by\u0142y dost\u0119pne do odczytu i zapisu.<\/p>\n<h3>Tryb Autoheal<\/h3>\n<p>\nDzia\u0142a podobnie jak tryb Ignoruj, z wyj\u0105tkiem tego, \u017ce sam klaster automatycznie wybiera przegran\u0105 stron\u0119 po podziale i przywr\u00f3ceniu sp\u00f3jno\u015bci. Przegrana strona wraca do klastra pusta, a kolejka traci wszystkie wiadomo\u015bci, kt\u00f3re by\u0142y wysy\u0142ane tylko na t\u0119 stron\u0119.<\/p>\n<h3>Tryb Pauza Mniejszo\u015bci<\/h3>\n<p>\nJe\u015bli nie chcemy dopu\u015bci\u0107 do logicznego podzia\u0142u, naszym jedynym rozwi\u0105zaniem jest rezygnacja z odczytu i zapisu po mniejszej stronie po podziale klastra. Kiedy broker widzi, \u017ce znajduje si\u0119 po mniejszej stronie, wstrzymuje dzia\u0142anie, zamykaj\u0105c wszystkie istniej\u0105ce po\u0142\u0105czenia i odrzucaj\u0105c wszelkie nowe. Co sekund\u0119 sprawdza odzyskanie \u0142\u0105czno\u015bci. Gdy tylko \u0142\u0105czno\u015b\u0107 zostanie przywr\u00f3cona, wznawia dzia\u0142anie i ponownie \u0142\u0105czy si\u0119 z klastrem.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/7a51cf509b2c94e2ae100c7dedd9d21a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 22. Trzech wydawc\u00f3w jest po\u0142\u0105czonych z trzema brokerami. Wewn\u0105trz klaster kieruje wszystkie zapytania do g\u0142\u00f3wnej kolejki na Brokerze 2.<\/i><\/p>\n<p>Nast\u0119pnie Brokerzy 1 i 2 od\u0142\u0105czaj\u0105 si\u0119 od Brokera 3. Zamiast podnie\u015b\u0107 swoje lustro do roli mistrza, Broker 3 wstrzymuje dzia\u0142anie i staje si\u0119 niedost\u0119pny.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/c12ec38a47e1f8ccceebfa1854f54f21.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 23. Broker 3 wstrzymuje dzia\u0142anie, od\u0142\u0105cza wszystkich klient\u00f3w i odrzuca zapytania o po\u0142\u0105czenie.<\/i><\/p>\n<p>Gdy tylko \u0142\u0105czno\u015b\u0107 zostanie przywr\u00f3cona, wraca do klastra.<\/p>\n<p>Zobaczmy inny przyk\u0142ad, w kt\u00f3rym g\u0142\u00f3wna kolejka znajduje si\u0119 na Brokerze 3.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/4cc6a28270f00a280ecb5af5252f4e07.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 24. G\u0142\u00f3wna kolejka na Brokerze 3.<\/i><\/p>\n<p>Nast\u0119pnie nast\u0119puje ta sama utrata \u0142\u0105czno\u015bci. Broker 3 przechodzi w stan pauzy, poniewa\u017c znajduje si\u0119 po mniejszej stronie. Po drugiej stronie w\u0119z\u0142y widz\u0105, \u017ce Broker 3 odpad\u0142, wi\u0119c starsze lustro z Broker\u00f3w 1 i 2 podnosi si\u0119 do roli mistrza.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/d0d032385533bb53c5a95927afcd3119.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 25. Przej\u015bcie do Brokera 2 przy niedost\u0119pno\u015bci Brokera 3.<\/i><\/p>\n<p>Gdy \u0142\u0105czno\u015b\u0107 zostanie przywr\u00f3cona, Broker 3 do\u0142\u0105czy do klastra.<\/p>\n<p><img decoding=\"async\" alt=\"RabbitMQ vs Kafka: odporno\u015b\u0107 na awarie i wysoka dost\u0119pno\u015b\u0107 w klastrach\" src=\"\/wp-content\/uploads\/2019\/11\/53f8186a040ae07724ef59cf79b72a0c.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Rys. 26. Klastar wr\u00f3ci\u0142 do normalnego dzia\u0142ania.<\/i><\/p>\n<p>Wa\u017cne jest, aby zrozumie\u0107, \u017ce uzyskujemy sp\u00f3jno\u015b\u0107, ale mo\u017cemy r\u00f3wnie\u017c uzyska\u0107 dost\u0119pno\u015b\u0107, <i><b>je\u015bli<\/b><\/i> sukcesywnie przeniesiemy klient\u00f3w na wi\u0119ksz\u0105 cz\u0119\u015b\u0107 podzia\u0142u. W wi\u0119kszo\u015bci sytuacji osobi\u015bcie wybra\u0142bym tryb Pauza Mniejszo\u015b\u0107, ale to naprawd\u0119 zale\u017cy od konkretnego przypadku.<\/p>\n<p>Aby zapewni\u0107 dost\u0119pno\u015b\u0107, wa\u017cne jest, aby upewni\u0107 si\u0119, \u017ce klienci pomy\u015blnie \u0142\u0105cz\u0105 si\u0119 z w\u0119z\u0142em. Rozwa\u017cmy nasze opcje.<\/p>\n<h1>Zapewnienie \u0142\u0105czno\u015bci klient\u00f3w<\/h1>\n<p>\nMamy kilka sposob\u00f3w, jak po utracie po\u0142\u0105czenia skierowa\u0107 klient\u00f3w do g\u0142\u00f3wnej cz\u0119\u015bci klastra lub dzia\u0142aj\u0105cych w\u0119z\u0142\u00f3w (po awarii jednego w\u0119z\u0142a). Najpierw przypomnijmy sobie, \u017ce konkretna kolejka jest umieszczana na okre\u015blonym w\u0119\u017ale, ale routingu i polityki s\u0105 replikowane na wszystkich w\u0119z\u0142ach. Klienci mog\u0105 \u0142\u0105czy\u0107 si\u0119 z dowolnym w\u0119z\u0142em, a wewn\u0119trzne routingu skieruje ich tam, gdzie trzeba. Jednak gdy w\u0119ze\u0142 jest zawieszony, odrzuca po\u0142\u0105czenia, dlatego klienci musz\u0105 po\u0142\u0105czy\u0107 si\u0119 z innym w\u0119z\u0142em. Je\u015bli w\u0119ze\u0142 jest niedost\u0119pny, nie ma on za wiele do zaoferowania.<\/p>\n<p>Nasze opcje:<\/p>\n<ul>\n<li>Dost\u0119p do klastra odbywa si\u0119 za pomoc\u0105 load balancera, kt\u00f3ry po prostu cyklicznie przegl\u0105da w\u0119z\u0142y, a klienci pr\u00f3buj\u0105 si\u0119 po\u0142\u0105czy\u0107, a\u017c do sukcesu. Je\u015bli w\u0119ze\u0142 jest niedzia\u0142aj\u0105cy lub zawieszony, to pr\u00f3by po\u0142\u0105czenia z tym w\u0119z\u0142em zako\u0144cz\u0105 si\u0119 niepowodzeniem, ale kolejne pr\u00f3by skieruj\u0105 si\u0119 do innych serwer\u00f3w (w trybie cyklicznym). To rozwi\u0105zanie jest odpowiednie w przypadku kr\u00f3tkotrwa\u0142ej utraty po\u0142\u0105czenia lub awarii serwera, kt\u00f3ry szybko zostanie uruchomiony ponownie.\n<\/li>\n<li>Dost\u0119p do klastra przez load balancer i usuni\u0119cie zawieszonych\/niedost\u0119pnych w\u0119z\u0142\u00f3w z listy, jak tylko b\u0119d\u0105 wykryte. Je\u015bli uda si\u0119 to szybko zrobi\u0107 i je\u015bli klienci b\u0119d\u0105 w stanie spr\u00f3bowa\u0107 po\u0142\u0105czenia ponownie, to uzyskamy sta\u0142\u0105 dost\u0119pno\u015b\u0107.\n<\/li>\n<li>Da\u0107 ka\u017cdemu klientowi list\u0119 wszystkich w\u0119z\u0142\u00f3w, a klient przy po\u0142\u0105czeniu losowo wybiera jeden z nich. Je\u017celi przy pr\u00f3bie po\u0142\u0105czenia otrzyma b\u0142\u0105d, przechodzi do nast\u0119pnego w\u0119z\u0142a na li\u015bcie, a\u017c si\u0119 po\u0142\u0105czy.\n<\/li>\n<li>Usun\u0105\u0107 ruch od niedzia\u0142aj\u0105cego\/zawieszonego w\u0119z\u0142a za pomoc\u0105 DNS. Robi si\u0119 to przy u\u017cyciu ma\u0142ego TTL.<\/li>\n<\/ul>\n<p><\/p>\n<h1>Wnioski<\/h1>\n<p>\nKlastrowanie RabbitMQ ma swoje zalety i wady. Najpowa\u017cniejsze wady to:<\/p>\n<ul>\n<li>przy do\u0142\u0105czeniu do klastra w\u0119z\u0142y odrzucaj\u0105 swoje dane;\n<\/li>\n<li>blokuj\u0105ca synchronizacja prowadzi do niedost\u0119pno\u015bci kolejki.<\/li>\n<\/ul>\n<p>\nWszystkie trudne decyzje wynikaj\u0105 z tych dw\u00f3ch cech architektury. Gdyby RabbitMQ mog\u0142o przechowywa\u0107 dane przy ponownym nawi\u0105zywaniu po\u0142\u0105czenia w klastrze, synchronizacja by\u0142aby szybsza. Gdyby mia\u0142o mo\u017cliwo\u015b\u0107 niena\u0142adowej synchronizacji, lepiej obs\u0142ugiwa\u0142oby du\u017ce kolejki. Rozwi\u0105zanie tych dw\u00f3ch problem\u00f3w znacznie poprawi\u0142oby wydajno\u015b\u0107 RabbitMQ jako technologii wymiany wiadomo\u015bci o wysokiej dost\u0119pno\u015bci i odporno\u015bci na b\u0142\u0119dy. Nie odwa\u017cy\u0142bym si\u0119 poleca\u0107 RabbitMQ z klasteryzacj\u0105 w nast\u0119puj\u0105cych sytuacjach:<\/p>\n<ul>\n<li>Niezawodna sie\u0107.\n<\/li>\n<li>Niezawodne przechowywanie.\n<\/li>\n<li>Bardzo du\u017ce kolejki.<\/li>\n<\/ul>\n<p>\nJe\u015bli chodzi o ustawienia wysokiej dost\u0119pno\u015bci, rozwa\u017c takie:<\/p>\n<ul>\n<li><code>ha-promote-on-failure=always<\/code>\n<\/li>\n<li><code>ha-sync-mode=manual<\/code>\n<\/li>\n<li><code>cluster_partition_handling=ignore<\/code> (lub <code>autoheal<\/code>)\n<\/li>\n<li>trwa\u0142e wiadomo\u015bci\n<\/li>\n<li>upewnij si\u0119, \u017ce klienci \u0142\u0105cz\u0105 si\u0119 z aktywnym w\u0119z\u0142em, gdy jaki\u015b w\u0119ze\u0142 przestaje dzia\u0142a\u0107<\/li>\n<\/ul>\n<p>\nDla sp\u00f3jno\u015bci (bezpiecze\u0144stwa danych) rozwa\u017c nast\u0119puj\u0105ce ustawienia:<\/p>\n<ul>\n<li>Potwierdzenia wydawcy i r\u0119czne potwierdzenia po stronie konsumenta\n<\/li>\n<li><code>ha-promote-on-failure=when-synced<\/code>, je\u015bli wydawcy mog\u0105 spr\u00f3bowa\u0107 ponownie p\u00f3\u017aniej i je\u015bli masz bardzo niezawodne przechowywanie! W przeciwnym razie ustaw <code>=always<\/code>.\n<\/li>\n<li><code>ha-sync-mode=automatic<\/code> (ale dla du\u017cych nieaktywnych kolejek mo\u017ce by\u0107 wymagany tryb r\u0119czny; ponadto zastan\u00f3w si\u0119, czy niedost\u0119pno\u015b\u0107 nie doprowadzi do utraty wiadomo\u015bci)\n<\/li>\n<li>tryb Pauza Mniejszo\u015bci\n<\/li>\n<li>trwa\u0142e wiadomo\u015bci<\/li>\n<\/ul>\n<p>\nNie om\u00f3wili\u015bmy jeszcze wszystkich zagadnie\u0144 dotycz\u0105cych odporno\u015bci na b\u0142\u0119dy i wysokiej dost\u0119pno\u015bci; na przyk\u0142ad, jak bezpiecznie przeprowadza\u0107 procedury administracyjne (takie jak aktualizacje bez przestoj\u00f3w). Nale\u017cy r\u00f3wnie\u017c porozmawia\u0107 o federacji i wtyczce Shovel.<\/p>\n<p>Je\u015bli co\u015b jeszcze pomin\u0105\u0142em, daj mi zna\u0107.<\/p>\n<p>Zobacz tak\u017ce m\u00f3j <noindex><a rel=\"nofollow\" href=\"https:\/\/jack-vanlightly.com\/blog\/2018\/9\/10\/how-to-lose-messages-on-a-rabbitmq-cluster\">post<\/a><\/noindex>, w kt\u00f3rym dokonuj\u0119 rozbicia klastra RabbitMQ przy u\u017cyciu Dockera i Blockade, aby przetestowa\u0107 niekt\u00f3re scenariusze utraty wiadomo\u015bci opisane w tym artykule.<\/p>\n<p>Poprzednie artyku\u0142y z serii: <br \/>\nNr 1 \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/itsumma\/blog\/416629\/\">habr.com\/ru\/company\/itsumma\/blog\/416629<\/a><\/noindex> <br \/>\nNr 2 \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/itsumma\/blog\/418389\/\">habr.com\/ru\/company\/itsumma\/blog\/418389<\/a><\/noindex> <br \/>\nNr 3 \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/itsumma\/blog\/437446\/\">habr.com\/ru\/company\/itsumma\/blog\/437446<\/a><\/noindex><br \/>\n<br \/>\u0179r\u00f3d\u0142o: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/itsumma\/blog\/471858\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u044c \u0438 \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c \u2014 \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0442\u0435\u043c\u044b, \u0442\u0430\u043a \u0447\u0442\u043e \u043f\u043e\u0441\u0432\u044f\u0442\u0438\u043c RabbitMQ \u0438 Kafka \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0442\u0430\u0442\u044c\u0438. \u0414\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u043e RabbitMQ, \u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u2014 \u043e Kafka, \u0432 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0438 \u0441 RabbitMQ. \u0421\u0442\u0430\u0442\u044c\u044f \u0434\u043b\u0438\u043d\u043d\u0430\u044f, \u0442\u0430\u043a \u0447\u0442\u043e \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0439\u0442\u0435\u0441\u044c \u043f\u043e\u0443\u0434\u043e\u0431\u043d\u0435\u0435. \u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438, \u0441\u043e\u0433\u043b\u0430\u0441\u043e\u0432\u0430\u043d\u043d\u043e\u0441\u0442\u0438 \u0438 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438 (HA), \u0430 \u0442\u0430\u043a\u0436\u0435 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441\u044b, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0438\u0434\u0442\u0438 \u0432 \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438. RabbitMQ \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-52525","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\".\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/rabbitmq-protiv-kafka-otkazoustojchivost-i-vysokaya-dostupnost-v-klasterah\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47RabbitMQ \u043f\u0440\u043e\u0442\u0438\u0432 Kafka: \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u044c \u0438 \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430\u0445 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\".\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/rabbitmq-protiv-kafka-otkazoustojchivost-i-vysokaya-dostupnost-v-klasterah\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-11-09T21:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-02-18T11:00:16+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47RabbitMQ vs Kafka: odporno\u015b\u0107 na b\u0142\u0119dy i wysoka dost\u0119pno\u015b\u0107 w klastrach | ProHoster","description":".","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/rabbitmq-protiv-kafka-otkazoustojchivost-i-vysokaya-dostupnost-v-klasterah","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47RabbitMQ \u043f\u0440\u043e\u0442\u0438\u0432 Kafka: \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u044c \u0438 \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430\u0445 | ProHoster","og:description":".","og:url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/rabbitmq-protiv-kafka-otkazoustojchivost-i-vysokaya-dostupnost-v-klasterah","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-11-09T21:00:00+00:00","article:modified_time":"2020-02-18T11:00:16+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"52525","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-24 03:54:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 20:41:25","updated":"2026-01-24 03:54:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/52525","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=52525"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/52525\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=52525"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=52525"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=52525"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}