В этой главе мы сосредоточимся на фундаментальных компонентах и понятиях, лежащих в основе Apache Kafka. Разберём, как устроена модель «топик — раздел — сообщение», что такое брокеры, какой принцип лежит в основе работы продьюсеров и консьюмеров, а также как все эти элементы взаимодействуют, чтобы обеспечить высокопроизводительную и надёжную шину событий. Понимание базовых концепций позволит системным аналитикам и проектировщикам интеграций грамотно использовать Kafka при построении EDA-архитектур.
2.1. Топики и разделы (topics & partitions)
2.1.1. Топик как канал обмена сообщениями
Топик в Kafka — это логический канал, в который (или из которого) системы публикуют и читают сообщения. Если проводить аналогию с традиционными брокерами сообщений (RabbitMQ, ActiveMQ), то топик напоминает «очередь» или «тему», но отличия кроются в механизмах хранения и масштабирования.
- Именование топиков: принято использовать названия, отражающие бизнес-контекст или тип отправляемых данных, например:
○
orders.created — события о созданных заказах;
○
payments.transactions — информация о платежах;
○
sensor.iot.temperature — данные от IoT-датчиков температуры.
- Логическая независимость: Продьюсер, публикующий сообщения в orders.created, не знает, какие приложения их читают. Консьюмеры, подписанные на orders.created, получают все события, не обращаясь к «отправителю» напрямую. Это снижает связанность систем и упрощает подключение новых получателей в будущем.
2.1.2. Партицирование (partitions)
Внутри каждого топика Kafka создаёт один или несколько
разделов (partitions). Разделы — это физические логи (log-файлы), расположенные на брокерах. Такой подход даёт несколько важных преимуществ:
1. МасштабированиеКаждый раздел может находиться на разных брокерах, тем самым топик «горизонтально» масштабируется. Если в топике 12 разделов, они могут быть распределены по 3−5 брокерам (каждый брокер хранит несколько разделов), и система будет эффективно работать при высоких нагрузках.
2. Параллелизм обработкиЕсли у топика несколько разделов, консьюмеры могут читать их одновременно. Каждый раздел назначается одному потоку обработки или инстансу приложения, тем самым достигается высокая пропускная способность.
3. Гарантия порядка в рамках разделаKafka гарантирует неизменяемый, упорядоченный список сообщений
внутри одного раздела. То есть сообщения записываются последовательно, сохраняя свою нумерацию (offset). Однако если топик состоит из нескольких разделов, то общий порядок для всех сообщений топика уже не гарантируется. Порядок соблюдается лишь в пределах каждого раздела.
2.1.3. Роль ключа (key) сообщения
Когда продьюсер отправляет сообщение в топик, он может задать
ключ (key). Kafka использует этот ключ, чтобы решить, в какой именно раздел (partition) отправить сообщение. Типичная стратегия партицирования: все сообщения с одинаковым ключом (например,
user_id) попадают в один и тот же раздел. Это позволяет:
- Сохранять локальный порядок для конкретного ключа. Например, все заказы одного пользователя будут приходить последовательно в один раздел.
- Избегать «распыления» связанных данных по разным разделам, что упрощает дальнейшую обработку.
Если ключ не задан, Kafka использует алгоритм «round-robin» («карусель») или псевдослучайное распределение, чтобы разгрузить разделы равномерно.
2.2. Сообщения (messages)
2.2.1. Состав сообщения
В Kafka
сообщение (message) — это ключевая единица данных. Оно содержит:
- Ключ (key) — может отсутствовать, но обычно рекомендуется использовать.
- Значение (value) — полезная нагрузка (payload). Может быть текстом, JSON, Avro, Protobuf или любым другим форматом.
- Заголовки (headers) — метаданные, задаваемые продьюсером (например, идентификатор корреляции, метки версий и т. п.).
- Таймстемп (timestamp) — Kafka проставляет метку времени при записи, чтобы отразить момент приёма сообщения брокером (или отправки продьюсером — зависит от настройки).
2.2.2. Offset (смещение)Каждое сообщение внутри раздела получает
уникальный порядковый номер —
offset. Нумерация идёт по возрастанию: первое записанное сообщение — offset 0, следующее — offset 1 и т. д. Offset — это критически важное понятие:
- Для самой Kafka offset помогает идентифицировать, какие сообщения хранятся в разделе и в каком порядке.
- Для консьюмера offset показывает, какие сообщения уже были прочитаны и обработаны (если он ведёт учёт своей «позиции»).
2.2.3. Retention (политика хранения)В классических брокерах сообщений (JMS, RabbitMQ) принято, что сообщение «исчезает» из очереди, как только доставлено получателю. В
Kafka же по умолчанию сообщения хранятся на диске в течение заданного времени (например, 7 дней) или до достижения определённого размера лог-файлов. Это называется
retention-политикой:
- Time-based retention: сообщения удаляются спустя, скажем, 7 дней.
- Size-based retention: если объём лог-файла превышает заданный лимит, самые старые сообщения удаляются.
- Compact topics (опционально): хранят только последнее сообщение с одинаковым ключом, удаляя предыдущие. Удобно, если в топике важно состояние (например, текущая цена на товар).
Такой механизм позволяет «возвращаться во времени» и повторно читать события при необходимости (например, для восстановления состояния системы или аналитики).
2.3. Продьюсеры (producers) и консьюмеры (consumers)
2.3.1. Продьюсер (producer)Продьюсер — это любая программа или сервис, который отправляет сообщения в топик. Он знает только следующие параметры:
- Список брокеров (bootstrap servers) — адреса, куда можно отправить метаданные и найти соответствующий лидер-раздел.
- Название топика — куда нужно публиковать.
- Партицирование — либо задано явно (через ключ), либо выбирается автоматически.
При публикации сообщения продьюсер может указать дополнительные настройки надёжности, например уровень подтверждения (acks, сокращение от «acknowledgements»). Часто встречаются следующие варианты:
- acks=0: продьюсер не ждёт подтверждения; быстрый, но потенциально ненадёжный способ (сообщения могут теряться).
- acks=1: ждём подтверждения от лидера раздела, что сообщение записано. Возможна небольшая потеря данных при сбое лидера.
- acks=all (или -1): ждём подтверждения от всех реплик, заданных в min.insync.replicas. Это самый надёжный режим, но он увеличивает задержку записи.
2.3.2. Консьюмер (consumer)Консьюмер — это программа, которая читает сообщения из топика. Подписываясь на один или несколько топиков, консьюмер получает
поток сообщений. Kafka позволяет консьюмерам хранить «смещение чтения» (offset), чтобы после перезапуска или сбоя консьюмер мог продолжить чтение «с того же места».
- Сессионная логика: при запуске консьюмер посылает «heartbeat» (пульс) запросы в Kafka, подтверждая, что он «жив». Если консьюмер отключается или зависает, брокер «передаст» его разделы другим консьюмерам в группе (см. далее про Consumer Groups).
- Ручная vs автокоммит: по умолчанию есть механизм автокоммита offset — консьюмер раз в определённый интервал отправляет брокеру информацию о том, до каких сообщений он дочитал. Однако иногда требуется ручное управление (особенно когда важно гарантировать, что сообщение действительно обработано бизнес-логикой).
2.4. Группы консьюмеров (Consumer Groups) и балансировка
2.4.1. Принцип работы групп консьюмеровОдним из наиболее мощных механизмов Kafka является
Consumer Group — набор консьюмеров, которые совместно читают один и тот же топик (или несколько топиков). При этом:
- Каждый раздел (partition) топика обрабатывается ровно одним консьюмером из группы. Это позволяет параллелить чтение: если в топике 8 разделов, можно запустить 4−8 консьюмеров, каждый будет обслуживать определённые разделы.
- Автоматическое перераспределение (rebalance): если один консьюмер «вышел из строя» или, наоборот, новый консьюмер присоединился к группе, Kafka автоматически «перераспределит» разделы.
- Отслеживание смещений (offsets): каждый консьюмер в группе ведёт учёт, какие сообщения уже «прочитал» и обработал, сохраняя offset. Kafka хранит эти offset’ы в специальном служебном топике.
2.4.2. Масштабирование и отказоустойчивость- Масштабирование по горизонтали: чтобы читать топик быстрее, достаточно увеличить количество партиций и запустить нужное число консьюмеров в группе.
- Высокая доступность: если один консьюмер падает, его разделы «переезжают» к другим работающим консьюмерам в группе. Система продолжает работу, хоть и может на короткое время приостановиться, пока идёт процесс ре-балансировки.
2.4.3. Пример использования группПредставим сервис, который обрабатывает заказы из топика
orders.new. Если в топике, скажем, 6 разделов, мы можем поднять
группу из 6 микросервисных инстансов (каждый инстанс — отдельный консьюмер). Тогда каждый получит «свой» раздел и будет обрабатывать поступающие туда заказы. Если рост нагрузки требует ещё больше скорости, мы можем увеличить число разделов до 12, а затем поднять 12 инстансов приложения.
2.5. Кластер Kafka и брокеры
2.5.1. Структура кластераКластер Kafka состоит из одного или нескольких
брокеров (broker). Брокер — это процесс (JVM), который:
- Хранит разделы топиков (log-файлы) на локальном диске.
- Обрабатывает запросы продьюсеров (запись сообщений) и консьюмеров (чтение сообщений).
- Общается с другими брокерами, чтобы поддерживать согласованность и репликацию.
Обычно для продакшн-систем выделяют
не менее трёх брокеров — это помогает достичь устойчивости к сбоям (если один брокер выйдет из строя, остальные продолжат работу).
2.5.2. Репликация партиций и роль лидераКаждый раздел (partition) может иметь
несколько реплик (копий). При этом один брокер назначается
лидером (leader) раздела, а остальные брокеры —
фолловерами (followers). Запись (write) от продьюсеров идёт только в лидера, а фолловеры асинхронно копируют данные. При сбое лидера, автоматический механизм контроллера кластера (ранее ZooKeeper, а в новых версиях — встроенный KRaft) выбирает нового лидера из числа фолловеров.
- Фактор репликации (replication.factor) задаёт, сколько копий будет храниться в кластере. Типовое значение в продакшне — 3.
- min.insync.replicas определяет минимальное число реплик, которые должны подтвердить запись сообщения, чтобы считать её успешной (в режиме acks=all).
2.5.3. Роль ZooKeeper и переход к KRaftИсторически Kafka использовала
ZooKeeper для хранения метаданных и координации (кто лидер, какие разделы на каких брокерах и т. п.). Начиная с версии 2.8, в Kafka появился режим
KRaft (Kafka Raft), который встроил механизмы управления метаданными напрямую в Kafka-брокеры, постепенно делая ZooKeeper не нужным.
- ZooKeeper-регион: при классической установке Kafka необходимо поднимать отдельный кластер ZooKeeper, отвечающий за «сердцебиение» и конфигурацию.
- KRaft: новый режим упрощает архитектуру, убирая дополнительную прослойку. При этом достигается та же отказоустойчивость, используя консенсусный протокол Raft непосредственно в брокерах Kafka.
На момент написания книги (актуальные версии Kafka в 2023—2024 гг.) в продакшн-системах всё ещё часто встречается ZooKeeper, но всё больше компаний переходит на KRaft.
2.6. Ключевые особенности, важные для аналитика и интегратора
2.6.1. Хранение сообщений и «воспроизведение»В отличие от обычных очередей, Kafka хранит сообщения
долгое время, что открывает возможности:
- Повторное воспроизведение (replay): можно «прокрутить» данные заново, если что-то пошло не так или необходимо восстановить систему из журнального лога.
- Late joiners: новые сервисы, подключающиеся в систему, могут «дочитывать» историю за нужный период (при условии, что сообщения не устарели по retention-политике).
2.6.2. Приоритизация нагрузки и партицированиеПри проектировании интеграций важно продумать:
- Количество разделов: определяет максимально доступную параллелизацию. Но чем больше разделов, тем выше нагрузка на кластер в плане метаданных и управления.
- Выбор ключа (partition key): если мы хотим гарантировать порядок для конкретных сущностей (например, заказов одного клиента), стоит использовать client_id или order_id в качестве ключа.
2.6.3. Масштабирование чтения (consumer groups)Когда необходимо увеличить скорость обработки сообщений, аналитик или архитектор может:
- Увеличить число разделов топика.
- Добавить дополнительные экземпляры (инстансы) приложения, входящие в одну Consumer Group.
Это практически не требует изменений логики продьюсеров — «шина» Kafka сама позаботится о балансировке.
2.6.4. Гибкость и слабая связанность- Слабая связанность (loose coupling): продьюсер не зависит от числа и типов консьюмеров, он просто «бросает» события в топик.
- Лёгкость интеграции: многие системы (БД, сервисы, NoSQL-хранилища) имеют готовые коннекторы (Kafka Connect), что облегчает интеграцию и уменьшает «костыли» при обмене данными.
2.7. Что дальше?
В этой главе мы познакомились с ключевыми элементами Kafka:
- Топиками и их разделами (partitions) — основа горизонтального масштабирования и хранение упорядоченных логов сообщений.
- Сообщениями (messages) — атомарными единицами данных, содержащими ключ, значение и метаданные.
- Продьюсерами и консьюмерами, обеспечивающими асинхронный обмен в режиме Pub/Sub.
- Группами консьюмеров (Consumer Groups), позволяющими распределять нагрузку и автоматически обрабатывать сбои в узлах.
- Брокерами, которые формируют кластер Kafka, и механизмом репликации, благодаря которому обеспечиваются надежность и отказоустойчивость.
Для системных аналитиков и проектировщиков межсистемных интеграций эти концепции крайне важны: они определяют,
как логически разбивать данные на топики,
как выбирать ключи для партицирования,
какой уровень гарантии доставки настраивать и
как планировать масштабирование.
В
следующих главах мы:
- Рассмотрим детальнее, как использовать Kafka в качестве центральной шины данных (data pipeline) и какие интеграционные паттерны и практики применять.
- Поговорим о гарантиях доставки (Exactly Once, At Least Once, транзакциях) и о том, как это влияет на дизайн систем.
- Углубимся в механизмы Kafka Connect, Kafka Streams и других инструментов экосистемы для полноценной построения событийно-ориентированной архитектуры.
Понимая внутреннее устройство Kafka, аналитики и интеграторы смогут принимать более взвешенные решения при проектировании: от выбора размера кластера и retention-политик до определения схем (Schema Registry) и способов преобразования данных. Именно это заложит основу успешной интеграции и дальнейшего развития корпоративной платформы обмена сообщениями.