Краткий обзор Apache Kafka

Thank you for reading this post, don't forget to subscribe!

Apache Kafka — бро­кер сооб­ще­ний, реа­ли­зу­ю­щий пат­терн Producer-Consumer с хоро­ши­ми спо­соб­но­стя­ми к гори­зон­таль­но­му мас­шта­би­ро­ва­нию или по-рус­ски рас­пре­де­лен­ная систе­ма пере­да­чи сооб­ще­ний, рас­счи­тан­ная на высо­кую про­пуск­ную спо­соб­ность. Это Open Source раз­ра­бот­ка, создан­ная ком­па­ни­ей LinkedIn на JVM сте­ке (Scala).

Гори­зон­таль­но мас­шта­би­руя какую-либо систе­му, вы поне­во­ле дела­е­те её рас­пре­де­лён­ной, а рабо­та с рас­пре­де­лён­ной систе­мой име­ет свои особенности.

Фор­маль­но, для опи­са­ния свойств рас­пре­де­лён­ных систем суще­ству­ет CAP-теорема.

Глав­ные досто­ин­ства Apache Kafka:

  • Ско­рость: один узел кла­сте­ра может обра­ба­ты­вать сот­ни мега­байт запи­сей в секунду.
  • Мас­шта­би­ру­е­мость: кла­стер мож­но про­зрач­но рас­ши­рять без про­стоя, пото­ки дан­ных партицированы.
  • Надеж­ность: сооб­ще­ния в кла­сте­ре реп­ли­ци­ро­ва­ны, каж­дый узел может содер­жать тера­бай­ты дан­ных без потерь в производительности.

В рас­пре­де­лён­ной систе­ме невоз­мож­но обес­пе­чить одно­вре­мен­ное выпол­не­ние всех трёх свойств: кон­си­стент­но­сти, доступ­но­сти, устой­чи­во­сти к сбо­ям узлов.

Что это за свойства:

Кон­си­стент­ность (Consistency)
Гово­рит о том, что систе­ма все­гда выда­ёт толь­ко логи­че­ски непро­ти­во­ре­чи­вые отве­ты. Не быва­ет тако­го, что вы доба­ви­ли в кор­зи­ну товар, а после реф­ре­ша стра­ни­цы его там не видите.
Доступ­ность (Availability)
Озна­ча­ет, что сер­вис отве­ча­ет на запро­сы, а не выда­ёт ошиб­ки о том, что он недоступен.
Устой­чи­вость к сбо­ям сети (Partition tolerance)
Озна­ча­ет, что рас­пре­де­лён­ная по кла­сте­ру систе­ма рабо­та­ет в рас­чё­те на слу­чаи про­из­воль­ной поте­ри паке­тов внут­ри сети.
С точ­ки зре­ния CAP-тео­ре­мы, Kafka име­ет CA*, т.е. выпол­ня­ют­ся усло­вия кон­си­стент­но­сти и доступ­но­сти, но не гаран­ти­ру­ет­ся устой­чи­вость к сбо­ям в сети — по отзы­вам поль­зо­ва­те­лей, Kafka не очень устой­чи­ва к netsplit (момен­ту, когда ваш кла­стер, напри­мер, раз­ва­ли­ва­ет­ся попо­лам), хотя офи­ци­аль­ной доку­мен­та­ции на этот счёт мы не нашли.

На самом низ­ком уровне Kafka — это про­сто рас­пре­де­лён­ный лог-файл. То есть, по сути, файл, раз­би­тый на несколь­ко частей (пар­ти­ций) и «рас­ка­тан­ный» на несколь­ко узлов кла­сте­ра. Запись в этот файл все­гда про­ис­хо­дит в конец. Раз­де­ле­ние фай­ла на части необ­хо­ди­мо для уско­ре­ния чте­ния из оче­ре­ди и гори­зон­таль­но­го мас­шта­би­ро­ва­ния. Ваш Topic (тема) может быть «поре­зан» на сколь­ко угод­но частей. Соот­вет­ствен­но, вы може­те раз­де­лить Topic на сколь­ко угод­но сер­ве­ров. Из каж­дой пар­ти­ции может читать не более одно­го Consumer (чита­тель). Это зна­чит, что мак­си­маль­ное чис­ло парал­лель­ных чита­те­лей рав­но коли­че­ству частей, на кото­рые раз­бит ваш Topic.

Соот­вет­ствен­но, для одной пар­ти­ции топи­ка гаран­ти­ру­ет­ся оче­рёд­ность сооб­ще­ний, так как из каж­дой пар­ти­ции может читать не более одно­го читателя.

У каж­до­го сооб­ще­ния есть свой сквоз­ной номер внут­ри пат­ри­ции. В тер­ми­нах Kafka это назы­ва­ет­ся offset. При чте­нии из пар­ти­ции чита­тель дела­ет ком­мит офф­се­та. Это необ­хо­ди­мо для того, что­бы, если, напри­мер, теку­щий чита­тель упа­дёт, то сле­ду­ю­щий (новый чита­тель) нач­нёт с послед­не­го коммита.

Чита­те­ли объ­еди­ня­ют­ся в груп­пы, что так и назы­ва­ет­ся — consumer group. При добав­ле­нии ново­го чита­те­ля или паде­нии теку­ще­го, груп­па пере­ба­лан­си­рут­ся. Это зани­ма­ет какое-то вре­мя, поэто­му луч­ший спо­соб чте­ния — под­клю­чить чита­те­ля и не пере­под­клю­чать его без необходимости.

Что каса­ет­ся доступ­но­сти, Kafka обес­пе­чи­ва­ет репли­ка­цию сооб­ще­ний и disk persistence, сохра­няя сооб­ще­ния на диск.

Фор­мат репли­ка­ции назы­ва­ет­ся InSync. Это зна­чит, что слей­вы (в тер­ми­нах Kafka это фол­ло­ве­ры) сами посто­ян­но спра­ши­ва­ют масте­ра о новых сооб­ще­ни­ях. Это pull-модель. Синхронностью/асинхронностью репли­ка­ции вы може­те управ­лять сами, ука­зы­вая какие гаран­тии (acknowledgement) вы хоти­те полу­чить при запи­си в оче­редь. Kafka под­дер­жи­ва­ет три режима:

  • отпра­вить и не дожи­дать­ся под­твер­жде­ния записи;
  • отпра­вить и дождать­ся под­твер­жде­ния на мастер-ноде;
  • отпра­вить и дождать­ся под­твер­жде­ния на всех репликах.

Вы долж­ны най­ти ком­про­мисс меж­ду воз­мож­но­стью поте­ри сооб­ще­ний и мини­маль­ным откли­ком при­ло­же­ния. Чем выше гаран­тии достав­ки, тем, соот­вет­ствен­но, доль­ше запись в очередь.

Посколь­ку Kafka гаран­ти­ру­ет кон­си­стент­ность, для чита­те­лей сооб­ще­ние будет вид­но толь­ко после запи­си по всем репли­кам. Репли­ка­ция про­ис­хо­дит отдель­но для каж­дой пар­ти­ции в топике.

Если вспом­нить про disk persistence, то он выте­ка­ет из устрой­ства Kafka. Так как вся систе­ма — это про­сто лог, то все сооб­ще­ния в любом слу­чае попа­да­ют на диск и это невоз­мож­но выклю­чить, но в кон­фи­гу­ра­ции мож­но под­кру­тить руч­ку, каки­ми пери­о­да­ми сооб­ще­ния пада­ют на диск. Что, соот­вет­ствен­но, умень­шит ваши гаран­тии на поте­рю сооб­ще­ний, но уве­ли­чит производительность.

Кли­ен­ты для Kafka доста­точ­но интел­лек­ту­аль­ные и рабо­та­ют на уровне TCP. В короб­ке с Kafka лежит кли­ент на Java (так как сама Kafka напи­са­на на Scala) и биб­лио­те­ка на C.

Для тех, кто пишет на .NET или, напри­мер, Python, суще­ству­ют open source бин­дин­ги к этой биб­лио­те­ке на С. Так как это open source раз­ра­бот­ки, исход­ный код у вас на руках. Дру­гое дело, что и пат­чить ино­гда при­дёт­ся вам.

Выво­ды: Apache Kafka менее удоб­на, чем тот же RabbitMQ, но если вы не може­те терять сооб­ще­ния, то вари­ант с Kafka под­хо­дит боль­ше. К тому же у Kafka гораз­до боль­ше scalability (рас­ши­ря­е­мость).

 

Для хра­не­ния мета­дан­ных в Kafka исполь­зу­ет­ся сер­вис под назва­ни­ем Zookeeper.

Zookeeper

– это рас­пре­де­лен­ное хра­ни­ли­ще клю­чей и зна­че­ний. Оно силь­но опти­ми­зи­ро­ва­но для счи­ты­ва­ния, но запи­си в нем про­ис­хо­дят мед­лен­нее. Чаще все­го Zookeeper при­ме­ня­ет­ся для хра­не­ния мета­дан­ных и обра­бот­ки меха­низ­мов кла­сте­ри­за­ции (пульс, рас­пре­де­лен­ные опе­ра­ции обновления/конфигурации, т.д.).

Таким обра­зом, кли­ен­ты это­го сер­ви­са (бро­ке­ры Kafka) могут на него под­пи­сы­вать­ся – и будут полу­чать инфор­ма­цию о любых изме­не­ни­ях, кото­рые могут про­изой­ти. Имен­но так бро­ке­ры узна­ют, когда веду­щий в сек­ции меня­ет­ся. Zookeeper исклю­чи­тель­но отка­зо­устой­чив (как и долж­но быть), посколь­ку Kafka силь­но от него зависит.

Он исполь­зу­ет­ся для хра­не­ния все­воз­мож­ных мета­дан­ных, в частности:

  • Сме­ще­ние групп потре­би­те­лей в рам­ках сек­ции (хотя, совре­мен­ные кли­ен­ты хра­нят сме­ще­ния в отдель­ной теме Kafka)
  • ACL (спис­ки кон­тро­ля досту­па) — исполь­зу­ют­ся для огра­ни­че­ния досту­па /авторизации
  • Кво­ты гене­ра­то­ров и потре­би­те­лей — мак­си­маль­ные пре­дель­ные коли­че­ства сооб­ще­ний в секунду
  • Веду­щие сек­ций и уро­вень их работоспособности

Когда стоит использовать Kafka?

Kafka поз­во­ля­ет про­пус­кать через цен­тра­ли­зо­ван­ную сре­ду огром­ное коли­че­ство сооб­ще­ний, а затем хра­нить их, не бес­по­ко­ясь о про­из­во­ди­тель­но­сти и не опа­са­ясь, что дан­ные будут потеряны.

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

Kafka поз­во­ля­ет с лег­ко­стью раз­гра­ни­чить ком­му­ни­ка­цию меж­ду раз­лич­ны­ми (микро)сервисами.