Apache Kafka — брокер сообщений, реализующий паттерн Producer-Consumer с хорошими способностями к горизонтальному масштабированию. Это Open Source разработка, созданная компанией LinkedIn на JVM стеке (Scala).
Горизонтально масштабируя какую-либо систему, вы поневоле делаете её распределённой, а работа с распределённой системой имеет свои особенности.
Формально, для описания свойств распределённых систем существует CAP-теорема.
В распределённой системе невозможно обеспечить одновременное выполнение всех трёх свойств: консистентности, доступности, устойчивости к сбоям узлов.
Что это за свойства:
- Консистентность (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 разработки, исходный код у вас на руках. Другое дело, что и патчить иногда придётся вам.
Мы работаем на платформе .NET, поэтому примеры кода выкладываю под .NET: примеры простой записи-чтения из шины.
Выводы: Apache Kafka менее удобна, чем тот же RabbitMQ, но если вы не можете терять сообщения, то вариант с Kafka подходит больше. К тому же у Kafka гораздо больше scalability (расширяемость).