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

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

В мінулым артыкуле мы разгледзелі кластарызацыю RabbitMQ для забеспячэння адмоваўстойлівасці і высокай даступнасці. Цяпер глыбока пакапаемся ў Apache Kafka.

Тут адзінкай рэплікацыі з'яўляецца раздзел (partition). У кожнага топіка адзін ці некалькі раздзелаў. У кожным раздзеле ёсць лідэр з фалаверамі або без іх. Пры стварэнні топіка паказваецца колькасць раздзелаў і каэфіцыент рэплікацыі. Звычайнае значэнне 3, гэта азначае тры рэплікі: адзін лідэр і два фалоўеры.

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

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

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

Збой падзелу

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

З сеткі сыходзіць брокер 3 - і для часткі 2 абіраецца новы лідэр на брокеры 2.

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

Затым сыходзіць брокер 1 і раздзел 1 таксама губляе свайго лідэра, роля якога пераходзіць да брокера 2.

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

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

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

Калі паднімаецца брокер 3, мы вяртаемся да трох рэплікаў на падзел. Але ўсе лідэры па-ранейшаму на брокеры 2.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 5. Незбалансаванае размяшчэнне лідэраў пасля аднаўлення брокераў 1 і 3

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

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

Каб выправіць гэта, Kafka прапануе два варыянты:

  • опцыя auto.leader.rebalance.enable=true дазваляе вузлу кантролера аўтаматычна перапрызначыць лідэраў назад на пераважныя рэплікі і тым самым аднавіць раўнамернае размеркаванне.
  • Адміністратар можа запусціць скрыпт kafka-preferred-replica-election.sh для перапрызначэння ўручную.

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

Гэта была спрошчаная версія збою, але рэальнасць больш складаная, хаця нічога занадта складанага тут няма. Усё зводзіцца да сінхранізаваных рэплікаў (In-Sync Replicas, ISR).

Сінхранізаваныя рэплікі (ISR)

ISR – гэта набор рэплік падзелу, які лічыцца "сінхранізаваным" (in-sync). Тут ёсць лідэр, а фолавераў можа не быць. Фаловер лічыцца сінхранізаваным, калі ён зрабіў дакладныя копіі ўсіх паведамленняў лідэра да заканчэння інтэрвалу replica.lag.time.max.ms.

Фаловер выдаляецца з набору ISR, калі ён:

  • не зрабіў запыт на выбарку за інтэрвал replica.lag.time.max.ms (лічыцца мёртвым)
  • не паспеў абнавіцца за інтэрвал replica.lag.time.max.ms (лічыцца павольным)

Фалаверы робяць запыты на выбарку ў інтэрвале replica.fetch.wait.max.ms, Які па змаўчанні складае 500 мс.

Каб дакладна растлумачыць мэту ISR, трэба паглядзець на пацверджанні ад вытворцы (producer) і некаторыя сцэнары адмовы. Вытворцы могуць выбраць, калі брокер адпраўляе пацверджанне:

  • acks=0, пацверджанне не адпраўляецца
  • acks=1, пацверджанне адпраўляецца пасля таго, як лідэр запісаў паведамленне ў свой лакальны лог
  • acks=all, пацверджанне адпраўляецца пасля таго, як усе рэплікі ў ISR запісалі паведамленне ў лакальныя логі

У тэрміналогіі Kafka, калі ISR захаваў паведамленне, адбываецца яго "комміт". Acks=all - самы бяспечны варыянт, але і дадатковая затрымка. Разгледзім два прыклады адмовы і як розныя опцыі 'acks' узаемадзейнічаюць з канцэпцыяй ISR.

Acks=1 і ISR

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

У гэтым прыкладзе ў вытворцы ўсталявана значэнне acks=1. Раздзел размеркаваны па ўсіх трох брокерам. Брокер 3 адстае, ён сінхранізаваўся з лідэрам восем секунд таму і зараз адстае на 7456 паведамленняў. Брокер 1 адстаў за ўсё на адну секунду. Наш вытворца адпраўляе паведамленне і хутка атрымлівае назад ack, без оверхеда на павольных ці мёртвых фолавераў, якіх лідэр не чакае.

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

Брокер 2 выходзіць са строю, і вытворца атрымлівае памылку злучэння. Пасля пераходу лідэрства да брокера 1 мы губляем 123 паведамленні. Фаловер на брокеры 1 уваходзіў у ISR, але не цалкам сінхранізаваўся з лідэрам, калі той упаў.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 8. Пры збоі губляюцца паведамленні

У канфігурацыі bootstrap.servers у вытворцы пералічана некалькі брокераў, і ён можа спытаць іншага брокера, хто стаў новым лідэрам падзелу. Затым ён устанаўлівае злучэнне з брокерам 1 і працягвае адпраўляць паведамленні.

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

Брокер 3 адстае яшчэ больш. Ён робіць запыты на выбарку, але не можа сінхранізавацца. Гэта можа быць злучана з павольным сеткавым злучэннем паміж брокерамі, праблемай захоўвання і т. д. Ён выдаляецца з ISR. Цяпер ISR складаецца з адной рэплікі – лідэра! Вытворца працягвае адпраўляць паведамленні і атрымліваць пацверджанні.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 10. Фаловер на брокеры 3 выдаляецца з ISR

Брокер 1 падае, і роля лідэра пераходзіць да брокера 3 з стратай 15286 паведамленняў! Вытворца атрымлівае паведамленне аб памылцы падключэння. Пераход да лідэра за межамі ISR быў магчымы толькі з-за наладкі unclean.leader.election.enable=true. Калі яна ўстаноўлена ў ілжывы, то пераход бы не адбыўся, а ўсе запыты чытання і запісы былі б адхіленыя. У гэтым выпадку мы чакаем вяртання брокера 1 з яго некранутымі дадзенымі ў рэпліцы, якая зноў возьме на сябе лідэрства.

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

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

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 12. Пасля кароткага перапынку паведамленні зноў адпраўляюцца ў раздзел 0

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

Acks=all і ISR

Давайце паўторым гэты сцэнар яшчэ раз, але з acks=all. Затрымка брокера 3 у сярэднім чатыры секунды. Вытворца адпраўляе паведамленне з acks=all, і зараз не атрымлівае хуткі адказ. Лідэр чакае, пакуль паведамленне захаваюць усе рэплікі ў ISR.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 13. ISR з трыма рэплікамі. Адна працуе павольна, што прыводзіць да затрымкі запісу

Пасля чатырох секунд дадатковай затрымкі брокер 2 адпраўляе ack. Усе рэплікі зараз поўнасцю абноўлены.

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

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

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 15. Рэпліка на брокеры 3 выдаляецца з ISR

Затым падае брокер 2, і лідэрства пераходзіць да брокера 1 без страты паведамленняў.

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

Вытворца знаходзіць новага лідэра і пачынае дасылаць яму паведамленні. Затрымка яшчэ памяншаецца, бо зараз ISR складаецца з адной рэплікі! Таму опцыя acks=all не дадае надмернасці.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 17. Рэпліка на брокеры 1 бярэ на сябе лідэрства без страты паведамленняў

Затым падае брокер 1, і лідэрства пераходзіць да брокера 3 са стратай 14238 паведамленняў!

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

Мы маглі б не ўсталёўваць опцыю unclean.leader.election.enable у значэнне праўда. Па змаўчанні яно роўна ілжывы. Настройка acks=all с unclean.leader.election.enable=true забяспечвае даступнасць з некаторай дадатковай бяспекай дадзеных. Але, як вы бачыце, мы ўсё яшчэ можам страціць паведамленні.

Але што, калі мы жадаем павялічыць бяспеку дадзеных? Можна паставіць unclean.leader.election.enable = false, Але гэта не абавязкова абароніць нас ад страты дадзеных. Калі лідэр упаў жорстка і панёс з сабой дадзеныя, то паведамленні па-ранейшаму страчаныя, плюс губляецца даступнасць, пакуль адміністратар не адновіць сітуацыю.

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

Acks=all, min.insync.replicas і ISR

З канфігурацыяй топіка min.insync.replicas мы падвышаем узровень бяспекі дадзеных. Давайце яшчэ раз пройдземся па апошняй частцы мінулага сцэнара, але на гэты раз з min.insync.replicas=2.

Такім чынам, у брокера 2 ёсць лідэр рэплікі, а фаловер на брокеры 3 выдалены з ISR.

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

Брокер 2 падае, а лідэрства пераходзіць да брокера 1 без страты паведамленняў. Але зараз ISR складаецца толькі з адной рэплікі. Гэта не адпавядае мінімальнай колькасці для атрымання запісаў, і таму брокер адказвае на спробу запісу памылкай NotEnoughReplicas.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 20. Лік ISR на адзін ніжэй, чым паказана ў min.insync.replicas

Гэтая канфігурацыя ахвяруе даступнасцю дзеля ўзгодненасці. Перш чым пацвердзіць паведамленне, мы гарантуем, што яно запісваецца прынамсі на дзве рэплікі. Гэта дае вытворцу значна большую ўпэўненасць. Тут страта паведамленняў магчымая толькі ў выпадку адначасовага збою двух рэплік у кароткі інтэрвал, пакуль паведамленне не рэплікавана дадатковаму фолаверу, што малаверагодна. Але калі вы суперпараноік, то можаце ўсталяваць каэфіцыент рэплікацыі 5, а min.insync.replicas на 3. Тут адразу тры брокеры павінны ўпасці адначасова, каб страціць запіс! Вядома, за такую ​​надзейнасць вы заплаціце дадатковай затрымкай.

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

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

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

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

Сэнс ISR

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

Мы самі выбіраем значэнне replica.lag.time.max.ms у адпаведнасці са сваімі патрэбамі. Па сутнасці гэты параметр азначае, якую затрымку мы гатовы прыняць пры acks=all. Значэнне па змаўчанні - дзесяць секунд. Калі для вас гэта задоўга, можаце яе паменшыць. Тады вырасце частата змен у ISR, паколькі фолаверы будуць часцей выдаляцца і дадавацца.

У RabbitMQ проста набор люстэркаў, якія трэба рэплікаваць. Павольныя люстэркі прыўносяць дадатковую затрымку, а водгуку мёртвых люстэркаў можна чакаць да заканчэння часу жыцця пакетаў, якія правяраюць даступнасць кожнага вузла (net tick). ISR - цікавы спосаб пазбегнуць гэтых праблем з павелічэннем затрымкі. Але мы рызыкуем страціць надмернасць, паколькі ISR можа скараціцца толькі да лідэра. Каб пазбегнуць гэтай рызыкі, выкарыстоўвайце настройку min.insync.replicas.

Гарантыя падключэння кліентаў

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

У RabbitMQ кліенты могуць падлучацца да любога вузла, а ўнутраная маршрутызацыя адпраўляе запыт куды трэба. Гэта азначае, што вы можаце ўсталяваць перад RabbitMQ балансавальнік нагрузкі. Kafka патрабуе, каб кліенты падключаліся да вузла, на якім размяшчаецца лідэр адпаведнай часткі. У такой сітуацыі балансавальнік нагрузкі не паставіць. Спіс bootstrap.servers крытычна важны, каб кліенты маглі звяртацца да патрэбных вузлоў і знаходзіць іх пасля збою.

Архітэктура кансэнсусу Kafka

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

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

Zookeeper захоўвае стан кластара:

  • Спіс топікаў, раздзелаў, канфігурацыю, бягучыя рэплікі лідэра, пераважныя рэплікі.
  • Члены кластара. Кожны брокер пінг ў кластар Zookeeper. Калі той не атрымлівае пінг на працягу зададзенага перыяду часу, то Zookeeper запісвае брокера ў недаступныя.
  • Выбар асноўнага і запаснога вузлоў для кантролера.

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

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

Для кожнага раздзела кантролер:

  • абнаўляе інфармацыю ў Zookeeper аб ISR і лідэры;
  • адпраўляе каманду LeaderAndISRCommand кожнаму брокеру, які размяшчае рэпліку гэтай часткі, інфармуючы брокераў аб ISR і лідэру.

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

Кожны лідэр адказвае за набор ISR. Настройка replica.lag.time.max.ms вызначае, хто туды ўвойдзе. Пры змене ISR лідэр перадае Zookeeper новую інфармацыю.

Zookeeper заўсёды інфармаваны аб любых зменах, каб у выпадку збою кіраўніцтва плаўна перайшло да новага лідэра.

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

Пратакол рэплікацыі

Разуменне дэталяў рэплікацыі дапамагае лепш зразумець патэнцыйныя сцэнары страты даных.

Запыты на выбарку, Log End Offset (LEO) і Highwater Mark (HW)

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

Лідэр і ўсе фолаверы захоўваюць зрушэнне канца лога (Log End Offset, LEO) і пазнаку Highwater (HW). Адзнака LEO захоўвае зрушэнне апошняга паведамлення ў лакальнай рэпліцы, а HW – зрушэнне апошняга комміта. Памятайце, што для статусу «комміт» паведамленне павінна быць захавана ва ўсіх рэпліках ISR. Гэта азначае, што LEO звычайна крыху апярэджвае HW.

Калі лідэр атрымлівае паведамленне, ён захоўвае яго лакальна. Фаловер робіць запыт на выбарку, перадаўшы свой LEO. Потым лідэр адпраўляе пакет паведамленняў, пачынаючы з гэтага LEO, а таксама перадае бягучы HW. Калі лідэр атрымлівае інфармацыю, што ўсе рэплікі захавалі паведамленне з зададзеным зрушэннем, ён перамяшчае адзнаку HW. Толькі лідэр можа перамясціць HW, і так усе фолаверы даведаюцца бягучае значэнне ў адказах на свой запыт. Гэта азначае, што фолаверы могуць адставаць ад лідэра і па паведамленнях, і адносна веды HW. Спажыўцы атрымліваюць паведамленні толькі да бягучага HW.

Звярніце ўвагу, што "захаваны" (persisted) азначае запісаны ў памяць, а не на дыск. Для прадукцыйнасці, Kafka выконвае сінхранізацыю на дыск з пэўным інтэрвалам. У RabbitMQ таксама ёсць такі інтэрвал, але ён адправіць пацверджанне паблішэру толькі пасля таго, як майстар і ўсе люстэркі запісалі паведамленне на дыск. Распрацоўнікі Kafka па меркаваннях прадукцыйнасці прынялі рашэнне адпраўляць ack, як толькі паведамленне запісана ў памяць. Kafka робіць стаўку на тое, што надмернасць кампенсуе рызыку кароткатэрміновага захоўвання пацверджаных паведамленняў толькі ў памяці.

Збой лідэра

Калі падае лідэр, Zookeeper паведамляе кантролер, і той выбірае новую рэпліку лідэра. Новы лідэр устанаўлівае новую адзнаку HW у адпаведнасці са сваім LEO. Затым інфармацыю аб новым лідэры атрымліваюць фолаверы. У залежнасці ад версіі Kafka, фаловер абярэ адзін з двух сцэнарыяў:

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

Фаловер можа спатрэбіцца зрэзаць лог па наступных прычынах:

  • Калі адбываецца збой лідэра, першы фаловер з набору ISR, зарэгістраваны ў Zookeeper, выйграе выбары і становіцца лідэрам. Усе фолаверы ў ISR, хоць і лічацца «сінхранізаванымі», маглі і не атрымаць ад былога лідэра копіі ўсіх паведамленняў. Цалкам магчыма, што ў абранага фаловера не самая актуальная копія. Kafka гарантуе, што паміж рэплікамі няма разыходжання. Такім чынам, каб пазбегнуць разыходжанні, кожны фаловер павінен усячы свой лог да значэння HW новага лідэра на момант яго абрання. Гэта яшчэ адна прычына, чаму настройка acks=all так важна для ўзгодненасці.
  • Паведамленні перыядычна запісваюцца на дыск. Калі ўсе вузлы кластара адмовілі адначасова, то на дысках захаваюцца рэплікі з розным зрушэннем. Цалкам магчыма, што калі брокеры зноў вернуцца ў сетку, новы лідэр, які будзе абраны, апынецца ззаду сваіх фолавераў, таму што ён захаваўся на дыск раней за іншых.

Уз'яднанне з кластарам

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

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

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

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

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

Ніжэй прыведзены некалькі сцэнарыяў парушэння складнасці:

  • Сцэнар 1. Фаловер не бачыць лідэра, але ўсё яшчэ бачыць Zookeeper.
  • Сцэнар 2. Лідэр не бачыць ніводнага фаловера, але ўсё яшчэ бачыць Zookeeper.
  • Сцэнар 3. Фаловер бачыць лідэра, але не бачыць Zookeeper.
  • Сцэнар 4. Лідэр бачыць фолавераў, але не бачыць Zookeeper.
  • Сцэнар 5. Фоловер цалкам аддзелены і ад іншых вузлоў Kafka, і ад Zookeeper.
  • Сцэнар 6. Лідэр цалкам аддзелены і ад іншых вузлоў Kafka, і ад Zookeeper.
  • Сцэнар 7. Вузел кантролера Kafka не бачыць іншы вузел Kafka.
  • Сцэнар 8. Кантролер Kafka не бачыць Zookeeper.

Для кожнага сцэнарыя прадугледжаны свае паводзіны.

Сцэнар 1. Фаловер не бачыць лідэра, але ўсё яшчэ бачыць Zookeeper

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

Парушэнне складнасці аддзяляе брокера 3 ад брокераў 1 і 2, але не ад Zookeeper. Брокер 3 больш не можа адпраўляць запыты на выбарку. Па заканчэнні часу replica.lag.time.max.ms ён выдаляецца з ISR і не ўдзельнічае ў комітах паведамленняў. Як толькі складнасць адноўлена, ён адновіць запыты на выбарку і далучыцца да ISR, калі дагоніць лідэра. Zookeeper працягне атрымліваць пінгі і лічыць, што брокер жывы і здаровы.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 23. Сцэнар 1. Брокер выдаляецца з ISR, калі ад яго не атрыманы запыт на выбарку на працягу інтэрвалу replica.lag.time.max.ms

Няма ніякага лагічнага падзелу (split-brain) ці прыпынення вузла, як у RabbitMQ. Замест гэтага памяншаецца надмернасць.

Сцэнар 2. Лідэр не бачыць ніводнага фаловера, але ўсё яшчэ бачыць Zookeeper

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 24. Сцэнар 2. Лідэр і два фаловеры

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

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 25. Сцэнар 2. ISR сцяўся толькі да лідэра

Сцэнар 3. Фаловер бачыць лідэра, але не бачыць Zookeeper

Фаловер адлучаецца ад Zookeeper, але не ад брокера з лідэрам. У выніку фаловер працягвае рабіць запыты выбаркі і быць чальцом ISR. Zookeeper больш не атрымлівае пінгі і рэгіструе падзенне брокера, але паколькі гэта толькі фаловер, няма ніякіх наступстваў пасля аднаўлення.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 26. Сцэнар 3. Фоловер працягвае адпраўляць лідэру запыты на выбарку

Сцэнар 4. Лідэр бачыць фолавераў, але не бачыць Zookeeper

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 27. Сцэнар 4. Лідэр і два фаловеры

Лідэр аддзелены ад Zookeeper, але не ад брокераў з фалаверамі.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 28. Сцэнар 4. Лідэр ізаляваны ад Zookeeper

Праз некаторы час Zookeeper зарэгіструе падзенне брокера і паведаміць пра гэта кантролер. Той выбера сярод фолавераў новага лідэра. Аднак зыходны лідэр будзе працягваць думаць, што ён з'яўляецца лідэрам і будзе працягваць прымаць запісы з acks=1. Фалаверы больш не адпраўляюць яму запыты на выбарку, таму ён палічыць іх мёртвымі і паспрабаваць сціснуць ISR да самога сябе. Але паколькі ў яго няма падлучэння да Zookeeper, ён не зможа гэта зрабіць, і ў гэты момант адмовіцца ад наступнага прыёму запісаў.

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

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

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 29. Сцэнар 4. Лідэр на брокеры 1 становіцца фолаверам пасля аднаўлення сеткі

Сцэнар 5. Фоловер цалкам аддзелены і ад іншых вузлоў Kafka, і ад Zookeeper

Фаловер цалкам ізаляваны і ад іншых вузлоў Kafka, і ад Zookeeper. Ён проста выдаляецца з ISR, пакуль сетка не адновіцца, а потым даганяе астатніх.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 30. Сцэнар 5. Ізаляваны фаловер выдаляецца з ISR

Сцэнар 6. Лідэр цалкам аддзелены і ад іншых вузлоў Kafka, і ад Zookeeper

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 31. Сцэнар 6. Лідэр і два фаловеры

Лідэр цалкам ізаляваны ад сваіх фолавераў, кантролера і Zookeeper. На працягу кароткага перыяду ён працягне прымаць запісы з acks=1.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 32. Сцэнар 6. Ізаляцыя лідэра ад іншых вузлоў Kafka і Zookeeper

Не атрымаўшы запытаў па заканчэнні replica.lag.time.max.ms, ён паспрабуе сціснуць ISR да самога сябе, але не зможа гэтага зрабіць, паколькі няма сувязі з Zookeeper, тады ён спыніць прымаць запісы.

Між тым, Zookeeper адзначыць ізаляванага брокера як мёртвага, а кантролер абярэ новага лідэра.

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 33. Сцэнар 6. Два лідэры

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

RabbitMQ супраць Kafka: адмоваўстойлівасць і высокая даступнасць
Мал. 34. Сцэнар 6. Вытворцы перамыкаюцца на новага лідэра

Будуць страчаныя ўсе пацверджаныя запісы, зробленыя зыходным лідэрам з моманту страты складнасці. Як толькі сетка адноўлена, зыходны лідэр праз Zookeeper выявіць, што больш не зьяўляецца лідэрам. Затым усячэ свой лог да HW новага лідэра на момант абрання і пачне адпраўляць запыты як фаловер.

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

У гэтай сітуацыі на працягу кароткага перыяду можа назірацца лагічны падзел, але толькі калі acks=1 и min.insync.replicas таксама 1. Лагічны падзел аўтаматычна завяршаецца або пасля аднаўлення сеткі, калі зыходны лідэр разумее, што ён больш не лідэр, або калі ўсе кліенты разумеюць, што лідэр змяніўся і пачынаюць пісаць новаму лідэру - у залежнасці ад таго, што адбудзецца раней. У любым выпадку адбудзецца страта некаторых паведамленняў, але толькі з acks=1.

Існуе іншы варыянт гэтага сцэнара, калі непасрэдна перад падзелам сеткі фолаверы адсталі, а лідэр сціснуў ISR да аднаго сябе. Затым ён ізалюецца з-за страты складнасці. Выбіраецца новы лідэр, але першапачатковы лідэр працягвае прымаць запісы, нават acks=all, таму што ў ISR акрамя яго нікога няма. Гэтыя запісы будуць страчаныя пасля аднаўлення сеткі. Адзіны спосаб пазбегнуць такога варыянту - min.insync.replicas = 2.

Сцэнар 7. Вузел кантролера Kafka не бачыць іншы вузел Kafka

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

Сцэнар 8. Кантролер Kafka не бачыць Zookeeper

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

Высновы са сцэнарыяў

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

Калі з-за страты складнасці лідэр аддзяліўся ад Zookeeper, гэта можа прывесці да страты паведамленняў з acks=1. Адсутнасць сувязі з Zookeeper выклікае кароткачасовы лагічны падзел з двума лідэрамі. Гэтую праблему вырашае параметр acks=all.

Параметр min.insync.replicas у дзве ці больш рэплік дае дадатковыя гарантыі, што такія кароткатэрміновыя сцэнары не прывядуць да страты паведамленняў, як у сцэнары 6.

Рэзюмэ па страце паведамленняў

Пералічоны ўсе спосабы, як можна страціць дадзеныя ў Kafka:

  • Любы збой лідэра, калі паведамленні пацвярджаліся з дапамогай acks=1
  • Любы нячысты (unclean) пераход лідэрства, гэта значыць на фаловера за межамі ISR, нават з acks=all
  • Ізаляцыя лідэра ад Zookeeper, калі паведамленні пацвярджаліся з дапамогай acks=1
  • Поўная ізаляцыя лідэра, які ўжо сціснуў гурт ISR да самога сябе. Будуць страчаныя ўсе паведамленні, нават acks=all. Гэта дакладна толькі ў тым выпадку, калі min.insync.replicas=1.
  • Адначасовыя збоі ўсіх вузлоў часткі. Паколькі паведамленні пацвярджаюцца з памяці, некаторыя могуць яшчэ не запісацца на кружэлку. Пасля перазагрузкі сервераў некаторых паведамленняў можа не хапаць.

Нячыстых пераходаў лідэрства можна пазбегнуць, або забараніўшы іх, або забяспечыўшы надмернасць не менш за два. Найбольш трывалая канфігурацыя - гэта спалучэнне acks=all и min.insync.replicas больш за 1.

Прамое параўнанне надзейнасці RabbitMQ і Kafka

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

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

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

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

Kafka робіць стаўку на тое, што калі паведамленне захоўваецца на некалькіх вузлах, можна пацвярджаць паведамленні, як толькі яны патрапілі ў памяць. З-за гэтага ўзнікае рызыка страты паведамленняў любога тыпу (нават acks=all, min.insync.рэплікі=2) у выпадку адначасовай адмовы.

У цэлым Kafka дэманструе больш высокую прадукцыйнасць па і першапачаткова спраектавана для кластараў. Колькасць фолавераў можна павялічыць да 11-ці, калі гэта трэба для надзейнасці. Каэфіцыент рэплікацыі 5 і мінімальны лік рэплік у сінхранізаваным стане min.insync.replicas=3 зробяць страту паведамлення вельмі рэдкай падзеяй. Калі ваша інфраструктура здольная забяспечыць такі каэфіцыент рэплікацыі і ўзровень надмернасці, то можаце абраць гэты варыянт.

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

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

Нарэшце, не забывайце аб шэрагу багаў у механізмах кластарызацыі і рэплікацыі як у RabbitMQ, так і ў Kafka. З часам сістэмы сталі больш сталымі і стабільнымі, але ніводнае паведамленне ніколі не будзе на 100% абаронена ад страты! Акрамя таго, у дата-цэнтрах здараюцца буйнамаштабныя аварыі!

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

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

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

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

Крыніца: habr.com

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