Зачем нужна Kafka
Kafka — это распределённая платформа для обмена сообщениями (часто её называют брокером сообщений). Звучит сложно? Согласен. Давайте разбираться на простом примере.
Представьте интернет-магазин. У нас есть два сервиса:
-
Сервис, который отвечает за остатки товаров на складе.
-
Сервис, который отправляет письма покупателям на электронную почту при покупке.
Когда пользователь совершает покупку, сервис остатков должен сообщить сервису уведомлений о необходимости отправить письмо. Первое, что приходит в голову новичку, — использовать REST API.
Правильно ли это? Нет.
Проблема синхронного взаимодействия
REST — это синхронное взаимодействие. Сервису остатков придётся ждать ответа от сервиса уведомлений, прежде чем продолжить работу. Это неэффективно.
Гораздо логичнее просто отправить "сигнал" и двигаться дальше. Такой подход называется асинхронной коммуникацией, а сами "сигналы" — сообщениями или событиями.
Как помогает Kafka
Здесь на сцену выходит Kafka. Она становится промежуточным звеном между сервисами:
-
Сервис остатков отправляет сообщение в Kafka и сразу продолжает работу
-
Сервис уведомлений забирает сообщение тогда, когда ему удобно
Именно поэтому Kafka называют брокером сообщений — она выступает посредником между сервисами.
Основные термины и возможности Kafka
Базовые понятия
Давайте разберём терминологию Kafka на простой аналогии с почтой:
-
Топик (Topic) — это "почтовый ящик" для определённого типа сообщений
-
Продюсер (Producer) — "почтальон", который кладёт сообщения в ящик
-
Консьюмер (Consumer) — "получатель", который забирает сообщения из ящика
В нашем примере:
-
Producer — сервис остатков товаров
-
Consumer — сервис уведомлений
-
Topic —
email_notifications
Ключевые преимущества
1. Множество "почтовых ящиков"
Kafka поддерживает множество топиков. Например:
-
payment_events— события об оплате -
email_notifications— уведомления на почту -
inventory_updates— обновления остатков
2. Несколько получателей для одного ящика
Представьте, что мы добавили сервис аналитики для сбора метрик. Теперь при покупке товара сообщение в топике email_notifications должны получить:
-
Сервис уведомлений (отправит письмо)
-
Сервис аналитики (посчитает метрики)
Kafka позволяет нескольким консьюмерам независимо читать сообщения из одного топика. При этом сообщение не исчезает после прочтения одним консьюмером.
Это основа Event-Driven Architecture (EDA) — архитектуры, где сервисы не знают друг о друге и общаются через общие события.
Kafka в сравнении с другими брокерами сообщений
Помимо Kafka существуют и другие брокеры сообщений. Давайте сравним её с довольно популярным RabbitMQ.
Kafka vs RabbitMQ
RabbitMQ (классическая очередь):
-
Сообщение удаляется после обработки
-
Работает по принципу "задача-исполнитель"
Kafka (распределённый журнал):
-
Сообщения хранятся заданное время
-
Многие потребители могут независимо читать одни и те же данные
В двух словах: Kafka — это не просто очередь, а “журнал событий” для масштабных систем. Она не только доставляет сообщения, но и хранит историю событий, позволяя пересматривать их заново.
Так зачем же вам Kafka?
Apache Kafka нужна, если:
-
Вы хотите разделить сильносвязанные сервисы — сервисы общаются только через события
-
Вам важна масштабируемость — можно добавлять потребителей без переписывания кода
-
Вы не можете терять данные — сообщения хранятся и могут быть перечитаны
-
Вы работаете с потоками данных — аналитика, мониторинг, уведомления
Погружаемся в работу Kafka чуть глубже
Пытливый читатель наверняка задавался следующими вопросами по ходу статьи:
-
Как выглядит это самое сообщение, которое мы шлём в Kafka
-
Почему в начале Kafka названа распределённой платформой?
-
За счёт чего Kafka делает возможным независимое чтение сообщений разными консьюмерами?
-
И так далее
На эти и другие вопросы я постараюсь ответить в следующих разделах.
Как выглядит Kafka-сообщение
Kafka-сообщение — не что иное, как набор пар ключ-значение. Примером может послужить старый добрый JSON.
Давайте снова вернёмся к нашему примеру с сервисом остатков на складе и сервисом уведомлени.
Представим, что в письме покупателю мы хотим указать название купленного товара и его количество. Посмотрим, как должно выглядеть сообщение, которое мы положим в Kafka.
{
"email": "customer@example.com",
"product_name": "some_product",
"quantity": "some_quantity"
}
Вот такого набора пар ключ-значение нам достаточно для того, чтобы решить поставленную выше задачу.
Как устроен топик в Kafka и при чём тут масштабируемость
может показаться, что топик в Kafka — единая очередь сообщений. На самом деле, это не совсем так. «Под капотом» топик — это немного более сложная структура. Давайте разбираться
Партиции — основа масштабируемости
Топик логически делится на части — партиции (partitions). Можно представить себе топик как книгу, где каждая партиция — это отдельная глава. Все главы принадлежат одной книге (топику), но каждая имеет свою собственную, независимую нумерацию страниц (последовательность сообщений).
Возникает резонный вопрос: зачем такие сложности? Ответ прост: именно это решение делает Kafka масштабируемой, отказоустойчивой и быстрой.
Проблема одного сервера на приложение
Представим, что у нас есть 4 сервера: 3 сервера с нашими сервисами из нашего примера с интернет магазином, 1 сервер с Kafka.
Разумеется, это неправильный подход. Если, скажем, сервер, на котором работает сервис по управлению остатками, упадёт, то мы не сможем управлять остатками товаров.
Надо провести так называемую репликацию. Мы запускаем наши сервисы на других серверах, чтобы был "запасной аэродром" в случае падения основного сервера. Обычно рекомендуется запускать каждый сервис минимум на трёх серверах для достижения отказоустойчивости.
Kafka — это кластер серверов
У вас, вероятно, возник вопрос, зачем я всё это рассказываю. Так вот, эта вся история применима и к Kafka.
Kafka — это не одно приложение, запущенное на сервере. Это целый кластер серверов с запущенной на них Kafka. Это помогает нам достичь той же самой отказоустойчивости, как в случае с сервисами.
Проблема «бутылочного горлышка»: мир без партиций
Давайте представим, что наш топик email_notifications — это неделимая сущность, живущая на одном сервере Kafka в кластере из трёх машин.
Что происходит, когда наш интернет-магазин становится популярным?
-
Тысячи сообщений о покупках летят в один топик.
-
Весь этот поток данных обрушивается на один-единственный сервер, который является лидером для этого топика.
-
Остальные два сервера в кластере простаивают, лишь храня реплики, не помогая с обработкой входящего потока.
-
Сервер-лидер не выдерживает нагрузки, и запись сообщений замедляется или полностью останавливается. Это и есть классическое «бутылочное горлышко».
Как партиции решают проблему
Вот здесь на сцену и выходят партиции. Мы можем разбить наш топик email_notifications на 3 партиции и распределить их по кластеру.
До разбиения на партиции наш кластер выглядел следующим образом:
Server 1: email_notifications (весь топик, вся нагрузка) Server 2: реплика топика (нагрузка только на копирование) Server 3: реплика топика (нагрузка только на копирование)
После разбиения:
Server 1: email_notifications-0 (лидер)Server 2: email_notifications-1 (лидер)Server 3: email_notifications-2 (лидер)
Теперь входящие сообщения распределяются между тремя партициями, а значит, и между тремя серверами. Нагрузка записи перестала быть проблемой.
Новая проблема: надёжность
Но мы создали новую проблему. Теперь, если Server 1 выйдет из строя, мы полностью потеряем партицию email_notifications-0 и все её сообщения.
Репликация в Kafka
Kafka блестяще решает эту проблему с помощью репликаций. Каждая партиция может иметь несколько копий (реплик), распределённых по разным серверам. Количество этих копий задаётся фактором репликации (replication factor).
Давайте установим фактор репликации = 2 для нашего топика. Вот как будет выглядеть распределение:
Server 1: email_notifications-0 (лидер) | email_notifications-2(реплика)Server 2: email_notifications-1 (лидер) | email_notifications-0(реплика) Server 3: email_notifications-2 (лидер) | email_notifications-1(реплика)
В данном случае при падении одного из серверов все партиции останутся доступными (можете убедиться в этом сами, отбросив по очереди каждый сервер и считая доступные партиции). Также после падения сервера Kafka сама перераспределит положение партиций в серверах, выберет новых лидеров и т.п.
Лидеры и последователи: кто за что в ответе
Важно понимать, что в Kafka для каждой партиции работает модель «лидер-последователь» (Leader-Follower):
-
Лидер обрабатывает все запросы на запись и чтение для своей партиции.
-
Последователи (реплики) постоянно синхронизируются с лидером, «догоняя» его.
-
Если лидер выходит из строя, один из его последователей автоматически и мгновенно становится новым лидером.
Именно эта архитектура позволяет распределить нагрузку. В нашем примере каждый сервер является лидером для двух партиций и репликой для двух других, эффективно используя ресурсы.
Итог: что дают партиции и репликация на практике
-
Масштабируемость: Нагрузка на запись распределяется между многими серверами.
-
Параллелизм: Несколько продюсеров и консьюмеров могут работать с разными партициями одновременно.
-
Отказоустойчивость: Данные защищены даже при выходе из строя части серверов.
-
Производительность: Чтение и запись происходят с нескольких узлов одновременно, а не с одного.
Партиции + репликация — это фундамент, на котором строятся все высокопроизводительные и надёжные системы на базе Apache Kafka.
Consumer Groups
Масштабирование потребления
Вспомним наш пример с высоконагруженным интернет-магазином.
Нагрузку на Kafka мы распределили. Но что, если наш сервис уведомлений начнёт "тормозить" из-за большого числа писем, которые нужно отправить. Звучит не очень приятно. Однако, с помощью Kafka можно разрешить и эту проблему.
Помните, мы реплицировали наш сервис на дополнительные 2 сервера. Давайте попробуем включить эти два сервера в работу, чтобы снизить нагрузку на основной сервер.
Ранее мы разбивали наш топик email_notifications на 3 партиции. То есть можно сделать так, чтобы каждый из серверов с сервисом уведомлений читал из одной партиции.
Добиться этого нам помогают consumer groups. Это механизм, который позволяет распределять нагрузку по чтению между несколькими консьюмерами.
Как работают Consumer Groups
Consumer Group — это группа консьюмеров, которые работают вместе для обработки сообщений из топика. Kafka гарантирует, что каждая партиция топика будет обрабатываться только одним консьюмером в группе.
В нашем примере с 3 партициями и 3 серверами уведомлений:
Сервер 1 (Consumer 1): читает из Partition 0Сервер 2 (Consumer 2): читает из Partition 1Сервер 3 (Consumer 3): читает из Partition 2
Ключевые преимущества Consumer Groups:
-
Автоматическое распределение нагрузки — Kafka сама распределяет партиции между консьюмерами в группе
-
Отказоустойчивость — если один консьюмер падает, его партиции автоматически перераспределяются между оставшимися
-
Горизонтальное масштабирование — можно добавлять новые консьюмеры в группу, чтобы обрабатывать больше сообщений
Как Kafka хранит сообщения
Давайте теперь разберёмся, как Kafka хранит сообщения, которые были в неё отправлены.
Каждая партиция в Kafka — это не что иное, как журнал (log), куда новые сообщения добавляются в конец. При этом удалять и изменять эти сообщения вручную нельзя. Можно только добавлять.
Лог можно представить как длинный список записей
offset | message ---------------------- 0 | {...}1 | {...}2 | {...}...
Offset в данном случае - это порядковый номер сообщения в партиции.
Kafka хранит эти журналы на диске особым образом: каждая партиция разбита на сегменты (segment files). Эти сегменты имеют некоторый лимит. При его достижении, Kafka создаёт для партиции новый сегмент и пишет туда. Также заполненные сегменты могут быть удалены через некоторое время автоматически. Лимит сегмента и время его жизни зависят от настроек retention. С ними мы поиграемся в следующей статье.
Также стоит упомянуть о индексах, которые создаются для каждой партиции. Они хранятся рядом с .log файлами.
Бывают 2 вида индексов:
-
.index— для поиска по offset -
.timeIndex— для поиска по времени
Благодаря этим индексам консьюмер может мгновенно перейти к нужному сообщению, не перечитывая весь лог подряд.
За счёт чего несколько консьюмеров могут читать независимо сообщения в одной партиции
В прошлом пункте мы посмотрели на структуру .log файла. В этом файле был так называемый offset. Консьюмеры просто хранят внутри себя значение offset и по нему получают доступ к нужной им информации. Его, разумеется, можно настроить вручную в настройках консьюмера. Мы это обязательно сделаем в следующий раз.
Как Kafka управляет своими данными: брокеры и контроллеры
Сервер с Kafka может быть: брокером, контроллером, или совмещать эти роли.
Брокер (Broker) — Рабочая лошадка
Брокер — это основа кластера Kafka. Если представить Kafka как распределённую базу данных, то брокер — это один её узел (нода).
Что делает брокер:
-
Хранит данные: На дисках брокера хранятся партиции топиков (те самые файлы с сообщениями).
-
Обрабатывает запросы: Принимает сообщения от продюсеров и отдаёт их консьюмерам.
-
Реплицирует данные: Для каждой партиции брокер может быть
-
Лидером: Обслуживает все операции чтения и записи для этой партиции.
-
Последователем (Follower): Тихо синхронизирует свои данные с лидером, готовый в любой момент заменить его.
-
Контроллер (Controller) — Мозг Kafka-кластера
Контроллер — это та единица Kafka-кластера, которая координирует его работу. Я в прошлых пунктах неоднократно упоминал автоматические действия, которые производит Kafka. Так вот. Это всё достигается именно благодаря контроллеру. Однако, контроллер тоже выбирается автоматически. Это может вызвать путаницу так как, казалось бы, нужен контроллер, чтобы выбрать контроллер. Вообще, выбор контроллера — это отдельная тема для разговора. Мы поговорим об этом в следующей статье. Сейчас же мы более подробно рассмотрим контроллер.
Что делает контроллер:
-
Выбор лидеров для всех партиций (leader election): Если какой-то брокер упал, контроллер переназначает его партиции на других брокеров, чтобы не останавливалась работа кластера.
-
Отслеживание состояния брокеров — кто жив, кто мёртв, кто только что присоединился. Это важно, чтобы знать, где есть актуальные реплики данных.
-
Синхронизацию метаданных: контроллер сообщает всем брокерам, какие партиции существуют, где у них лидеры и какие реплики нужно поддерживать в актуальном состоянии.
Иными словами, контроллер следит, чтобы в кластере всегда был порядок, и даже если часть брокеров "отвалилась", система продолжала работать без простоев.
То, как контроллеру всё это удаётся делать, тесно связано с автоматическим выбором контроллера. Как я и говорил ранее, об этом в следующий раз.
Подведение итогов
Kafka — это не просто брокер сообщений. Это распределённая, масштабируемая и отказоустойчивая платформа, которая позволяет строить современные системы на основе событий.
С её помощью:
-
Сервисы могут общаться асинхронно, не блокируя друг друга
-
Данные хранятся безопасно, их можно перечитывать и анализировать
-
Система легко масштабируется за счёт партиций и репликаций
-
Несколько потребителей могут независимо читать одно и то же событие, что упрощает интеграцию аналитики, мониторинга и уведомлений
Если вы хотите строить современные распределённые системы, где важны скорость, надёжность и масштабируемость, Kafka — это то, что вам нужно.
Именно поэтому её используют такие компании, как LinkedIn, Netflix, Uber, Airbnb, Spotify и многие другие. Эти сервисы обрабатывают миллиарды событий в реальном времени, и Kafka помогает им строить масштабируемую, отказоустойчивую и быструю инфраструктуру, где данные никогда не теряются.
В следующих статьях мы разберём практическую часть: как создавать продюсеров и консьюмеров на Spring Boot, работать с топиками и контролировать offset. А также разберём, как Kafka координирует работу кластера: от классического Zookeeper до современного KRaft
Автор: Mitohondriya
