RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах

Адмаўстойлівасць і высокая даступнасць – вялікія тэмы, так што прысвяцім RabbitMQ і Kafka асобныя артыкулы. Дадзены артыкул аб RabbitMQ, а наступная – аб Kafka, у параўнанні з RabbitMQ. Артыкул доўгі, так што ўладкоўвайцеся ямчэй.

Разгледзім стратэгіі адмоваўстойлівасці, узгодненасці і высокай даступнасці (HA), а таксама кампрамісы, на якія даводзіцца ісці ў кожнай стратэгіі. RabbitMQ можа працаваць на кластары вузлоў - і тады класіфікуецца як размеркаваная сістэма. Калі гаворка заходзіць аб размеркаваных сістэмах, мы часта гаворым аб узгодненасці і даступнасці.

Гэтыя паняцці апісваюць, як сістэма паводзіць сябе пры збоі. Збой сеткавага злучэння, збой сервера, збой цвёрдай кружэлкі, часавая недаступнасць сервера з-за зборкі смецця, страта пакетаў або запаволенне сеткавага злучэння. Усё гэта можа прывесці да страты дадзеных ці канфліктаў. Аказваецца, практычна немагчыма падняць сістэму, адначасова і цалкам несупярэчлівую (без страты дадзеных, без разыходжання дадзеных), і даступную (будзе прымаць аперацыі чытання і запісы) для ўсіх варыянтаў збою.

Мы ўбачым, што ўзгодненасць і даступнасць знаходзяцца на розных канцах спектра, і вам трэба абраць, у які бок аптымізаваць. Добрая навіна ў тым, што з RabbitMQ такі выбар магчымы. У вас гэтакія «нердаўскія» рычажкі, каб зрушваць баланс у бок большай узгодненасці ці большай даступнасці.

Адмысловая ўвага надамо таму, якія канфігурацыі прыводзяць да страты дадзеных з-за пацверджаных запісаў. Ёсць ланцужок адказнасці паміж паблішэрамі, брокерамі і спажыўцамі. Пасля таго, як паведамленне перададзена брокеру, гэта яго праца - не страціць паведамленне. Калі брокер пацвярджае паблішэр атрыманне паведамлення, мы не чакаем, што яно будзе страчана. Але мы ўбачым, што такое сапраўды можа адбыцца ў залежнасці ад канфігурацыі вашага брокера і выдаўца.

Прымітывы ўстойлівасці аднаго вузла

Устойлівыя чэргі/маршрутызацыя

У RabbitMQ два тыпу чаргі: працяглыя / ўстойлівыя (durable) і няўстойлівыя (non-durable). Усе чэргі захоўваюцца ў базе даных Mnesia. Устойлівыя чэргі паўторна аб'яўляюцца пры запуску вузла і, такім чынам, перажываюць перазапуск, збой сістэмы або збой сервера (датуль, пакуль захоўваюцца дадзеныя). Гэта азначае, што пакуль вы дэкларуеце маршрутызацыю (exchange) і чаргу ўстойлівымі, інфраструктура чэргаў/маршрутызацыі вернецца ў аператыўны рэжым.

Няўстойлівыя чэргі і маршрутызацыя выдаляюцца пры перазапуску вузла.

Устойлівыя паведамленні

Адно тое, што чарга даўгавечная, не азначае, што ўсе яе паведамленні перажывуць перазапуск вузла. Будуць адноўлены толькі паведамленні, устаноўленыя паблішэрам як устойлівыя (persistent). Устойлівыя паведамленні сапраўды ствараюць дадатковую нагрузку на брокера, але калі страта паведамлення непрымальная, то іншага выйсця няма.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 1. Матрыца ўстойлівасці

Кластарызацыя з люстраваннем чаргі

Каб перажыць страту брокера, нам патрэбна надмернасць. Можам аб'яднаць некалькі вузлоў RabbitMQ у кластар, а затым дадаць дадатковую надмернасць шляхам рэплікацыі чэргаў паміж некалькімі вузламі. Такім чынам, калі падае адзін вузел, мы не губляем дадзеныя і застаемся даступнымі.

Люстраванне чаргі:

  • адна галоўная чарга (майстар), якая атрымлівае ўсе каманды на запіс і чытанне
  • адно або некалькі люстэркаў, якія атрымліваюць усе паведамленні і метададзеныя з галоўнай чаргі. Гэтыя люстэркі існуюць не для маштабавання, а выключна для надмернасці.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 2. Люстэрка чаргі

Люстраванне устанаўліваецца адпаведнай палітыкай. У ёй можна абраць каэфіцыент рэплікацыі і нават вузлы, на якіх павінна размяшчацца чарга. Прыклады:

  • ha-mode: all
  • ha-mode: exactly, ha-params: 2 (адзін майстар і адно люстэрка)
  • ha-mode: nodes, ha-params: rabbit@node1, rabbit@node2

Пацвярджэнне паблішэра

Для дасягнення паслядоўнага запісу неабходны пацверджанні паблішэру (Publisher Confirms). Без іх ёсць верагоднасць страты паведамленняў. Пацверджанне адпраўляецца паблішэр пасля запісу паведамлення на дыск. RabbitMQ запісвае паведамленні на дыск не пры атрыманні, а на перыядычнай аснове, у раёне некалькіх сотняў мілісекунд. Калі чарга люструецца, пацверджанне адпраўляецца толькі пасля таго, як усе люстэркі таксама запісалі сваю копію паведамлення на дыск. Гэта азначае, што выкарыстанне пацверджанняў дадае затрымку, але калі бяспека дадзеных важная, то яны неабходныя.

Адмоўаўстойлівая чарга

Калі брокер завяршае працу або падае, усе кіроўныя чэргі (майстры) на гэтым вузле адвальваюцца разам з ім. Затым кластар выбірае самае старое люстэрка кожнага майстра і прасоўвае яго ў якасці новага майстра.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 3. Некалькі люстраваных чэргаў і іх палітыкі

Брокер 3 падае. Звярніце ўвагу, што люстэрка Чэргі З на Брокеры 2 павялічваецца да майстра. Таксама звернеце ўвагу, што для Чэргі C створана новае люстэрка на Брокеры 1. RabbitMQ заўсёды спрабуе падтрымліваць каэфіцыент рэплікацыі, указаны ў вашых палітыках.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 4. Брокер 3 адвальваецца, што выклікае адмову чаргі C

Падае наступны Брокер 1! У нас застаўся толькі адзін брокер. Да майстра павялічваецца люстэрка Чэргі B.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 5

Мы вярнулі Брокера 1. Незалежна ад таго, наколькі паспяхова дадзеныя перажылі страту і аднаўленне брокера, усе люстраваныя паведамленні чаргі адкідаюцца пры перазапуску. Гэта важна адзначыць, бо будуць наступствы. Мы хутка разгледзім гэтыя наступствы. Такім чынам, Брокер 1 зараз зноў з'яўляецца членам кластара, а кластар спрабуе выконваць палітыкі і таму стварае люстэркі на Брокеры 1.

У гэтым выпадку страта Брокера 1 была поўнай, як і дадзеных, таму нелюстраваная Чарга У страчана цалкам.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 6. Брокер 1 вяртаецца ў строй

Брокер 3 вярнуўся ў строй, так што чэргі A і B атрымліваюць назад створаныя на ім люстэркі, каб задаволіць сваім палітыкам HA. Але зараз усе галоўныя чэргі на адным вузле! Гэта не ідэальна, лепш раўнамернае размеркаванне паміж вузламі. Нажаль, тут няма адмысловых варыянтаў для перабалансавання майстроў. Вернемся да гэтай праблемы пазней, бо спачатку трэба разгледзець сінхранізацыю чаргі.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 7. Брокер 3 вяртаецца ў строй. Усе галоўныя чэргі на адным вузле!

Такім чынам, зараз у вас павінна быць уяўленне, як люстэркі забяспечваюць надмернасць і адмоваўстойлівасць. Гэта гарантуе даступнасць у выпадку адмовы аднаго вузла і абараняе ад страты даных. Але мы яшчэ не скончылі, таму што насамрэч усё значна складаней.

сінхранізацыя

Пры стварэнні новага люстэрка ўсё новыя паведамленні заўсёды будуць рэпліцыравацца на гэтае люстэрка і любыя іншыя. Што да існуючых дадзеных у галоўнай чарзе, мы можам іх рэплікаваць у новае люстэрка, якое становіцца поўнай копіяй майстра. Мы таксама можам не рэплікаваць існуючыя паведамленні і дазволіць галоўнай чарзе і новаму люстэрку збягацца ў часе, калі новыя паведамленні паступаюць у хвост, а існуючыя паведамленні сыходзяць з галавы галоўнай чаргі.

Такая сінхранізацыя выконваецца аўтаматычна ці ўручную і кіруецца з дапамогай палітыкі чэргаў. Разгледзім прыклад.

У нас дзве люстраваныя чэргі. Чарга A сінхранізуецца аўтаматычна, а Чарга B - уручную. У абедзвюх чэргах па дзесяць паведамленняў.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 8. Дзве чаргі з рознымі рэжымамі сінхранізацыі

Цяпер мы губляем Брокера 3.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 9. Брокер 3 упаў

Брокер 3 вяртаецца ў строй. Кластар стварае люстэрка для кожнай чаргі на новым вузле і аўтаматычна сінхранізуе новую Чаргу А з майстрам. Аднак люстэрка новай Чэргі В застаецца пустым. Такім чынам, у нас поўная надмернасць Чэргі A і толькі адно люстэрка для існуючых паведамленняў Чэргі B.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 10. Новае люстэрка Чэргі А атрымлівае ўсе існуючыя паведамленні, а новае люстэрка Чэргі B - не

У абедзве чаргі паступае яшчэ па дзесяць паведамленняў. Затым Брокер 2 падае, а Чарга А адкочваецца да самага старога люстэрка, якое знаходзіцца на Брокеры 1. Пры збоі не адбываецца страты дадзеных. У Чэргі B дваццаць паведамленняў у майстры і толькі дзесяць у люстэрку, паколькі гэтая чарга ніколі не рэпліцыравала зыходныя дзесяць паведамленняў.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 11. Чарга А адкочваецца Брокера 1 без страты паведамленняў

У абедзве чаргі паступае яшчэ па дзесяць паведамленняў. Цяпер падае Брокер 1. Чарга A без праблем перамыкаецца на люстэрка без страты паведамленняў. Аднак у Чэргі Ва ўзнікаюць праблемы. На гэтым этапе мы можам аптымізаваць альбо даступнасць, альбо ўзгодненасць.

Калі мы хочам аптымізаваць даступнасць, дык палітыку ha-promote-on-failure варта ўсталяваць у заўсёды. Гэта значэнне па змаўчанні, таму можна проста не ўказваць палітыку наогул. У такім выпадку, па сутнасці, мы дапускаем збоі ў несінхранізаваных люстэрках. Гэта прывядзе да страты паведамленняў, але чарга застаецца даступнай для чытання і запісы.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 12. Чарга А адкочваецца на Брокера 3 без страты паведамленняў. Чарга B адкочваецца на Брокера 3 са стратай дзесяці паведамленняў

Мы таксама можам усталяваць ha-promote-on-failure у значэнне when-synced. У гэтым выпадку замест адкату на люстэрка чарга будзе чакаць, пакуль Брокер 1 са сваімі дадзенымі вернецца ў аператыўны рэжым. Пасля яго вяртання галоўная чарга зноў аказваецца на Брокеры 1 без страты даных. Даступнасць прыносіцца ў ахвяру бяспекі даных. Але гэта рызыкоўны рэжым, які можа прывесці нават да поўнай страты даных, што мы разгледзім у бліжэйшы час.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 13. Чарга B застаецца недаступнай пасля страты Брокера 1

Вы можаце задаць пытанне: "Можа, лепш ніколі не выкарыстоўваць аўтаматычную сінхранізацыю?". Адказ заключаецца ў тым, што сінхранізацыя з'яўляецца блакіруючай аперацыяй. Падчас сінхранізацыі галоўная чарга не можа выконваць ніякіх аперацый чытання ці запісы!

Разгледзім прыклад. Цяпер у нас вельмі вялікія чэргі. Як яны могуць вырасці да такога памеру? Па некалькіх прычынах:

  • Чэргі не выкарыстоўваюцца актыўна
  • Гэта высакахуткасныя чэргі, а зараз спажыўцы працуюць павольна.
  • Гэта высакахуткасныя чэргі, адбыўся збой, і спажыўцы даганяюць

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 14. Дзве вялікія чэргі з рознымі рэжымамі сінхранізацыі

Цяпер падае Брокер 3.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 15. Брокер 3 падае, пакідаючы па адным майстру і люстэрку ў кожнай чарзе

Брокер 3 вяртаецца ў лад, і ствараюцца новыя люстэркі. Галоўная Чарга А пачынае рэплікаваць існуючыя паведамленні на новае люстэрка, і на працягу гэтага часу Чарга недаступная. Для рэплікацыі дадзеных патрабуецца дзве гадзіны, што прыводзіць да двух гадзін прастою для гэтай Чэргі!

Аднак Чарга B застаецца даступнай на працягу ўсяго перыяду. Яна ахвяравала некаторай надмернасцю дзеля даступнасці.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 16. Чарга застаецца недаступнай падчас сінхранізацыі

Праз дзве гадзіны Чарга A таксама становіцца даступнай і можа зноў пачаць прымаць аперацыі чытання і запісы.

Абнаўленні

Такія блакавальныя паводзіны падчас сінхранізацыі абцяжарваюць абнаўленне кластараў з вельмі вялікімі чэргамі. У нейкі момант вузел з майстрам трэба перазапусціць, што азначае або пераход на люстэрка, або адключэнне чаргі падчас абнаўлення сервера. Калі мы выберам пераход, то страцім паведамленні, калі люстэркі не сінхранізаваныя. Па змаўчанні падчас адключэння брокера пераход на несінхранізаванае люстэрка не выконваецца. Гэта азначае, што як толькі брокер вяртаецца, мы не губляем ніякіх паведамленняў, адзіным шкодай стаў толькі просты чарзе. Правілы паводзін пры адключэнні брокера задаюцца палітыкай ha-promote-on-shutdown. Можна ўсталяваць адно з двух значэнняў:

  • always= уключаны пераход на несінхранізаваныя люстэркі
  • when-synced= пераход толькі на сінхранізаванае люстэрка, інакш чарга становіцца недаступнай для чытання і запісы. Чарга вяртаецца ў строй, як толькі вернецца брокер

Так ці інакш, з вялікімі чэргамі даводзіцца выбіраць паміж стратай дадзеных і недаступнасцю.

Калі даступнасць павялічвае бяспеку дадзеных

Перш чым прымаць рашэнне, трэба ўлічыць яшчэ адно ўскладненне. Хоць аўтаматычная сінхранізацыя лепш для надмернасці, як яна ўплывае на бяспеку дадзеных? Вядома, дзякуючы лепшай надмернасці RabbitMQ з меншай верагоднасцю страціць існуючыя паведамленні, але што наконт новых паведамленняў ад паблішэраў?

Тут трэба ўлічыць наступнае:

  • Ці можа паблішэр проста вярнуць памылку, а вышэйстаячая служба або карыстач пазней паўтораць спробу?
  • Ці можа паблішэр захаваць паведамленне лакальна ці ў базе дадзеных, каб паўтарыць спробу пазней?

Калі паблішэр здольны толькі адкінуць паведамленне, то, насамрэч, паляпшэнне даступнасці таксама падвышае і бяспеку дадзеных.

Такім чынам трэба шукаць баланс, а рашэнне залежыць ад канкрэтнай сітуацыі.

Праблемы з ha-promote-on-failure=when-synced

Ідэя ha-promote-on-failure= when-synced складаецца ў тым, што мы прадухіляем пераключэнне на несінхранізаванае люстэрка і тым самым пазбягаем страты дадзеных. Чарга застаецца недаступнай для чытання ці запісы. Замест гэтага мы спрабуем вярнуць які ўпаў брокер з непашкоджанымі дадзенымі, каб ён аднавіў працу ў якасці майстра без страты дадзеных.

Але (і гэта вялікае але) калі брокер страціў свае дадзеныя, то ў нас вялікая праблема: чарга страчана! Усе дадзеныя зніклі! Нават калі ў вас ёсць люстэркі, якія ў асноўным даганяюць галоўную чаргу, гэтыя люстэркі таксама адкідаюцца.

Каб зноўку дадаць вузел з тым жа імем, мы гаворым кластару забыцца страчаны вузел (камандай rabbitmqctl forget_cluster_node) і запусціць новы брокер з тым жа імем хаста. Пакуль кластар памятае страчаны вузел, ён памятае старую чаргу і несінхранізаваныя люстэркі. Калі кластару кажуць забыцца страчаны вузел, гэтая чарга таксама забываецца. Цяпер трэба нанава яго аб'явіць. Мы страцілі ўсе дадзеныя, хоць у нас былі люстэркі з частковым наборам дадзеных. Лепш было б перайсці на несінхранізаванае люстэрка!

Таму ручная сінхранізацыя (і невыкананне сінхранізацыі) у спалучэнні з ha-promote-on-failure=when-synced, на мой погляд, даволі рызыкоўнае. Дакументы гавораць, што такі варыянт існуе для бяспекі даных, але гэта двухвостры нож.

Перабалансіроўка майстроў

Як і было абяцана, вяртаемся да праблемы збору ўсіх майстроў на адным або некалькіх вузлах. Гэта можа адбыцца нават у выніку «слізгальнага» (rolling) абнаўленні кластара. У кластары з трыма вузламі ўсе галоўныя чэргі назапасяцца на адным або двух вузлах.

Перабалансіроўка майстроў можа аказацца праблематычнай па двух прычынах:

  • Няма добрых інструментаў для выканання перабалансіроўкі
  • Сінхранізацыя чэргаў

Для перабалансіроўкі ёсць іншы убудова, які не падтрымліваецца афіцыйна. Адносна іншых плагінаў у кіраўніцтве RabbitMQ сказана: «Убудова падае некаторыя дадатковыя прылады налады і справаздачнасці, але не падтрымліваецца і не правераны камандай RabbitMQ. Выкарыстоўвайце на свой страх і рызыку».

Ёсць яшчэ адзін трук, каб перамясціць галоўную чаргу праз палітыкі HA. У кіраўніцтве згадваецца скрыпт для гэтага. Ён працуе наступным чынам:

  • Выдаляе ўсе люстэркі з дапамогай часовай палітыкі з больш высокім прыярытэтам, чым існуючая палітыка HA.
  • Змяняе часовую палітыку HA для выкарыстання рэжыму "вузлы" з указаннем вузла, на які патрабуецца перанесці галоўную чаргу.
  • Сінхранізуе чаргу для прымусовай міграцыі.
  • Пасля завяршэння міграцыі выдаляе часовую палітыку. Уступае ў дзеянне зыходная палітыка HA і ствараецца патрэбная колькасць люстэркаў.

Недахоп у тым, што такі падыход можа не спрацаваць, калі ў вас вялікія чэргі ці строгія патрабаванні да надмернасці.

Цяпер паглядзім, як кластары RabbitMQ працуюць з сеткавымі раздзеламі.

Парушэнне складнасці

Вузлы размеркаванай сістэмы злучаюцца сеткавымі сувязямі, а сеткавыя сувязі могуць і будуць адключацца. Частата адключэнняў залежыць ад лакальнай інфраструктуры або надзейнасці абранага аблокі. У любым выпадку, размеркаваныя сістэмы павінны быць у стане справіцца з імі. Зноў перад намі выбар паміж даступнасцю і ўзгодненасцю, і зноў добрая навіна ў тым, што RabbitMQ забяспечвае абодва варыянты (проста не адначасова).

З RabbitMQ у нас дзве асноўныя опцыі:

  • Дазволіць лагічны падзел (split-brain). Гэта забяспечвае даступнасць, але можа справакаваць страту даных.
  • Забараніць лагічны падзел. Можа прывесці да кароткатэрміновай страты даступнасці ў залежнасці ад спосабу падключэння кліентаў да кластара. Таксама можа прывесці да поўнай недаступнасці ў кластары з двух вузлоў.

Але што такое лагічны падзел? Гэта калі кластар падзяляецца напалам з-за страты сеткавых сувязяў. На кожным боку люстэрка павялічваюцца да майстра, так што ў выніку на кожную чаргу прыходзіцца некалькі майстроў.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 17. Галоўная чарга і два люстэркі, кожнае на асобным вузле. Затым узнікае сеткавы збой, і адно люстэрка адлучаецца. Аддзелены вузел бачыць, што два іншыя адваліліся, і прасоўвае свае люстэркі да майстра. Цяпер у нас дзве галоўныя чэргі, і абедзве дапускаюць запіс і чытанне.

Калі паблішэры адпраўляюць дадзеныя ў абодва майстры, у нас атрымаецца дзве разбежныя копіі чаргі.

Розныя рэжымы RabbitMQ забяспечваюць альбо даступнасць, альбо ўзгодненасць.

Рэжым Ignore (па змаўчанні)

Гэты рэжым забяспечвае даступнасць. Пасля страты складнасці адбываецца лагічны падзел. Пасля аднаўлення складнасці адміністратар павінен вырашыць, якой частцы аддаць перавагу. Які прайграў бок будзе перазапушчаны, і ўсе назапашаныя дадзеныя з гэтага боку губляюцца.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 18. Тры паблішэра звязаныя з трыма брокерамі. Унутрана кластар накіроўвае ўсе запыты ў галоўную чаргу на Брокеры 2.

Цяпер мы губляем Брокера 3. Ён бачыць, што іншыя брокеры адваліліся, і прасоўвае сваё люстэрка да майстра. Так адбываецца лагічны падзел.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 19. Лагічны падзел (split-brain). Запісы ідуць у дзве галоўныя чэргі, і дзве копіі разыходзяцца.

Сувязь аднаўляецца, але лагічны падзел застаецца. Адміністратар павінен уручную абраць які прайграў бок. У прыведзеным ніжэй выпадку адміністратар перазагружае Брокера 3. Губляюцца ўсе паведамленні, якія той не паспеў перадаць.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 20. Адміністратар адключае Брокера 3.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 21. Адміністратар запускае Брокера 3, і ён далучаецца да кластара, губляючы ўсе паведамленні, якія там заставаліся.

Падчас страты складнасці і пасля яе аднаўлення кластар і гэтая чарга былі даступныя для чытання і запісы.

Рэжым Autoheal

Працуе аналагічна рэжыму Ignore, за выключэннем таго, што сам кластар аўтаматычна выбірае які прайграў бок пасля падзелу і ўзнаўленні складнасці. Які прайграў бок вяртаецца ў кластар пусты, а чарга губляе ўсе паведамленні, якія былі адпраўленыя толькі на той бок.

Рэжым Pause Minority

Калі мы не жадаем дапусціць лагічнага падзелу, то наш адзіны варыянт - адмовіцца ад чытання і запісы на меншым баку пасля часткі кластара. Калі брокер бачыць, што знаходзіцца на меншым баку, то прыпыняе працу, гэта значыць закрывае ўсе існуючыя злучэнні і адмаўляецца ад любых новых. Адзін раз у секунду ён правярае аднаўленне складнасці. Як толькі складнасць адноўлена, ён аднаўляе працу і далучаецца да кластара.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 22. Тры паблішэра звязаныя з трыма брокерамі. Унутрана кластар накіроўвае ўсе запыты ў галоўную чаргу на Брокеры 2.

Затым Брокеры 1 і 2 адлучаюцца ад Брокера 3. Замест таго, каб падвышаць сваё люстэрка да майстра, Брокер 3 прыпыняе працу і становіцца недаступным.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 23. Брокер 3 прыпыняе працу, адключае ўсіх кліентаў і абвяргае запыты на падлучэнне.

Як толькі складнасць адноўлена, ён вяртаецца ў кластар.

Паглядзім на іншы прыклад, дзе галоўная чарга знаходзіцца на Брокеры 3.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 24. Галоўная чарга на Брокеры 3.

Затым адбываецца тая ж страта складнасці. Брокер 3 устае на паўзу, паколькі знаходзіцца на меншым баку. На іншым боку вузлы бачаць, што Брокер 3 адваліўся, так што больш старое люстэрка з Брокера 1 і 2 павялічваецца да майстра.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 25. Пераход да Брокера 2 пры недаступнасці Брокера 3.

Калі складнасць адноўлена, Брокер 3 далучыцца да кластара.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць у кластарах
Мал. 26. Кластар вярнуўся да нармальнай працы.

Тут важна разумець, што мы атрымліваем узгодненасць, але таксама можам атрымаць даступнасць, калі паспяхова перавядзем кліентаў на большую частку часткі. Для большасці сітуацый асабіста я абраў бы рэжым Pause Minority, але гэта рэальна залежыць ад канкрэтнага выпадку.

Для забеспячэння даступнасці важна пераканацца, што кліенты паспяхова падключаюцца да вузла. Разгледзім нашыя варыянты.

Забеспячэнне складнасці кліентаў

У нас некалькі варыянтаў, як пасля страты складнасці накіраваць кліентаў на асноўную частку кластара або на працуючыя вузлы (пасля збою аднаго вузла). Спачатку давайце ўспомнім, што канкрэтная чарга размяшчаецца на пэўным вузле, але маршрутызацыя і палітыкі рэпліцыруюцца на ўсіх вузлах. Кліенты могуць падлучацца да любога вузла, а ўнутраная маршрутызацыя накіруе іх куды трэба. Але калі вузел прыпынены, ён не прымае злучэнні, таму кліенты павінны падлучыцца да іншага вузла. Калі вузел адваліўся, ён увогуле мала што можа зрабіць.

Нашы варыянты:

  • Доступ да кластара ажыццяўляецца з дапамогай балансавальніка нагрузкі, які проста цыклічна перабірае вузлы, а кліенты выконваюць паўторныя спробы падключэння да паспяховага завяршэння. Калі вузел не працуе або спынены, то спробы падлучэння да гэтага вузла завершацца няўдачай, але наступныя спробы пойдуць на іншыя серверы (у цыклічным рэжыме). Гэта падыходзіць для кароткачасовай страты складнасці або ўпаў сервера, які будзе хутка падняты.
  • Доступ да кластара праз балансавальнік нагрузкі і выдаленне прыпыненых/упалых вузлоў з спісу, як толькі яны выяўлены. Калі хутка гэта зрабіць, і калі кліенты здольныя на паўторныя спробы падключэння, то мы атрымаем пастаянную даступнасць.
  • Даць кожнаму кліенту спіс усіх вузлоў, а кліент пры падключэнні выпадковым чынам выбірае адзін з іх. Калі пры спробе падключэння ён атрымлівае памылку, то пераходзіць да наступнага вузла ў спісе, пакуль не падключыцца.
  • Прыбраць трафік ад упалага/прыпыненага вузла з дапамогай DNS. Гэта робіцца з дапамогай малога TTL.

Высновы

У кластарызацыі RabbitMQ свае перавагі і недахопы. Найбольш сур'ёзныя недахопы заключаюцца ў тым, што:

  • пры далучэнні да кластара вузлы адкідаюць свае дадзеныя;
  • блакавальная сінхранізацыя прыводзіць да недаступнасці чаргі.

Усе цяжкія рашэнні выцякаюць з гэтых дзвюх асаблівасцяў архітэктуры. Калі б RabbitMQ мог захоўваць дадзеныя пры ўз'яднанні кластара, то сінхранізацыя адбывалася б хутчэй. Калі б ён быў здольны на неблакіруючую сінхранізацыю, то лепш падтрымліваў вялікія чэргі. Ухіленне гэтых двух праблем сур'ёзна палепшыла б характарыстыкі RabbitMQ у якасці адмоваўстойлівай і высокадаступнай тэхналогіі абмену паведамленнямі. Я б не рашыўся рэкамендаваць RabbitMQ з кластэрызацыяй ў наступных сітуацыях:

  • Ненадзейная сетка.
  • Ненадзейнае захоўванне.
  • Вельмі вялікія чэргі.

Што да налад для высокай даступнасці, то разгледзьце такія:

  • ha-promote-on-failure=always
  • ha-sync-mode=manual
  • cluster_partition_handling=ignore (Або autoheal)
  • устойлівыя паведамленні
  • пераканайцеся, што кліенты падключаюцца да актыўнага вузла, калі нейкі вузел выходзіць са строю.

Для ўзгодненасці (бяспекі дадзеных) разгледзьце наступныя наладкі:

  • Publisher Confirms і Manual Acknowledgements на баку спажыўца
  • ha-promote-on-failure=when-synced, Калі выдаўцы могуць паўтарыць спробу пазней і калі ў вас ёсць вельмі надзейнае сховішча! Інакш стаўце =always.
  • ha-sync-mode=automatic (але для вялікіх неактыўных чэргаў можа спатрэбіцца ручны рэжым; акрамя таго, падумайце, ці не прывядзе недаступнасць да страты паведамленняў)
  • рэжым Pause Minority
  • устойлівыя паведамленні

Мы разгледзелі яшчэ не ўсе пытанні адмоваўстойлівасці і высокай даступнасці; напрыклад, як бяспечна выконваць адміністрацыйныя працэдуры (такія, як слізгальныя абнаўленні). Трэба пагаварыць таксама аб федэраванні і плагіне Shovel.

Калі я яшчэ нешта выпусціў, калі ласка, дайце ведаць.

Глядзіце таксама мой пост, Дзе я ажыццяўляю пагром у кластары RabbitMQ з дапамогай Docker і Blockade, каб праверыць некаторыя сцэнары страты паведамленняў, апісаныя ў гэтым артыкуле.

Папярэднія артыкулы серыі:
№1 - habr.com/be/company/itsumma/blog/416629
№2 - habr.com/be/company/itsumma/blog/418389
№3 - habr.com/be/company/itsumma/blog/437446

Крыніца: habr.com

Дадаць каментар