Брокеры сообщений #
1. Плюсы и минусы брокеров сообщений #
Плюсы
- Асинхронность - службы могут работать независимо друг от друга
- Масштабируемость - легко увеличивать количество потребителей
- Надёжность - сообщения не теряются благодаря механизмам повторной отправки и подтверждений
- Разделение нагрузки - потребители могут обрабатывать сообщения параллельно
Минусы
- Сложность настройки - требует правильной конфигурации для эффективной работы
- Задержка - добавление брокера увеличивает задержку доставки сообщения
- Поддержка инфраструктуры - нужны усилия для мониторинга и администрирования
2. Разница между Kafka и RabbitMQ #
| Критерий | Kafka | RabbitMQ |
|---|---|---|
| Модель обмена | Публикация-подписка (pub-sub) | Точка-точка (point-to-point), публикация-подписка |
| Хранение сообщений | Сообщения хранятся на диске до истечения срока хранения | Удаляются после прочтения (по умолчанию) |
| Обработка сообщений | Pull-модель, потребители запрашивают сообщения | Push-модель, сообщения “проталкиваются” потребителям, когда те готовы их принять |
| Производительность | Выше | Ниже |
| Масштабируемость | Легко масштабируется горизонтально за счёт распределённой архитектуры | Масштабируемость ограничена по сравнению с Kafka, хотя можно использовать кластеризацию |
3. Что такое топик в Kafka? #
Топик в Kafka - это логический канал передачи сообщений. Состоит из разделов (партиций), которые представляют из себя FIFO-структуру. Разделение топика на разделы позволяет распределять нагрузку между брокерами, и обрабатывать их параллельно, обеспечивая масштабируемость и отказоустойчивость
4. Что такое Producer и что такое Consumer? #
Producer (производитель) - отправитель сообщений Consumer (потребитель) - получатель сообщений
Что даст увеличение количества консьюмеров? Увеличение числа консьюмеров в группе (consumer group) позволяет ускорить обработку данных, т.к. каждый консьюмер будет обрабатывать свою партицию одновременно с остальными консьюмерами. Также это позволяет распределить ответственность между разными компонентами.
Если консьюмеров меньше, чем партиций - они будут распределены через Round-Robin (или кастомный метод). Если консьюмеров больше, чем партиций - какие-то из консьюмеров будут простаивать
Пример Топик с 4 партициями
- Если в группе 2 потребителя, то каждый будет обрабатывать 2 партиции
- Если в группе 3 потребителя, то (по дефолту) один из них будет обрабатывать 2 партиции
- Если в группе 4 потребителя, каждый получит по 1 партиции
- Если в группе 5 потребителей, один из них останется без работы
5. Что такое partition в Kafka? #
Partition (партиция, раздел) - раздел топика, структура данных типа FIFO без возможности удаления элемента при чтении.
На что делится partition? Партиция не делится дальше, содержит записи/сообщения.
Запись/сообщение в партиции представляет из себя:
- (опционально) Key - ключ, используется для распределения сообщения по партициям, не обязан быть уникальным
- Value - данные/информация
- (опционально) Headers - заголовки, используются для передачи метаданных, которые по тем или иным причинам не могут быть занесены в key-value
- Timestamp - метка времени (создания или добавления в партицию в зависимости от настроек)
6. Распределение сообщений. Как сообщения распределяются по партициям? #
Продюсер определяет, в какой раздел отправить сообщение, используя следующие подходы:
Явное указание партиции - продюсер может явно указать, в какую партицию отправить сообщение
Выбор партиции на основании алгоритма (без явного указания)
- Если сообщение содержит ключ, Kafka использует хеширование ключа для выбора раздела. Это гарантирует, что все сообщения с одинаковым ключом будут попадать в один раздел
- Если ключ и явное указание партиции отсутствуют, используется стратегия Sticky Partitioning - все сообщения пишутся в одну партицию до достижения
batch.size(размер батча в байтах, по умолчанию равен 16384)
Примечание. Распространено заблуждение, что по умолчанию Kafka-продюсер распределяет сообщения по партициям, используя стратегию Round-Robin. Однако с версии 2.4 это не так ( документация). Выбрать стратегию Round-Robin можно при помощи явного указания
spring: kafka: producer: properties: partitioner.class: org.apache.kafka.clients.producer.RoundRobinPartitionerКастомный алгоритм - продюсер может реализовать собственную логику распределения сообщений по разделам (реализовав интерфейс
Partitioner)
7. Гарантии доставки в Kafka #
- At least once - сообщение будет доставлено как минимум один раз (может больше)
- At most once - сообщение будет доставлено максимум один раз (может не быть доставлено вовсе)
- Exactly once - сообщение будет доставлено ровно один раз. Это наиболее строгий уровень гарантии и требует согласованности между producer-ом и consumer-ом, чтобы избежать дублирования сообщений
Хабр. Философские размышления на тему exactly once
8. Какие существуют паттерны работы с очередями? #
- Point-to-Point (P2P) - один продюсер, один потребитель. Сообщение читается одним потребителем и удаляется из очереди
- Publish-Subscribe (Pub/Sub) - один продюсер отправляет сообщение нескольким подписчикам через обменники
- Work Queue - несколько потребителей обрабатывают сообщения из одной очереди, распределяя нагрузку
- Dead Letter Queue (DLQ) - очередь для сообщений, которые не удалось обработать
- Priority Queue - сообщения обрабатываются в зависимости от их приоритета
9. Основные компоненты Kafka? #
Apache Kafka - это распределённая система обработки потоков, состоящая из нескольких ключевых компонентов
- Producer (производитель) - отправляет данные в Kafka
- Consumer (потребитель) - получает данные из Kafka
- Topic (топик) - логическая сущность, разделяющая данные на каналы
- Partitions (партиции) - разделы топика, которые разделяют данные для их распределенного хранения и параллельной обработки. Партиции дают возможность хранить данные на разных брокерах
- Broker (брокер) - сервер, ответственный за хранение данных в Kafka
- Consumer Group (группа потребителей) - объединение консьюмеров, которые параллельно обрабатывают данные одного топика. Каждый консьюмер в группе обрабатывает разные партиции топика, что улучшает параллельность и производительность
- Controller - один из брокеров, который управляет метаданными кластера и отвечает за перераспределение партиций в случае отказа
- ZooKeeper - координатор, служит для управления и координации брокеров, поддержания метаданных системы. В новых версиях Kafka (с 3.7) вместо него используется KRaft (Kafka Raft). С версии 4.0 Zookeper удалён
10. Kafka. Consumer Group #
Consumer Group - это механизм для группировки консьюмеров, работающих с одним топиком, для:
- распределения нагрузки - партиции разделяются между консьюмерами группы, каждый читает данные только из своих партиций
- гибкости - можно увеличивать количество консьюмеров в группе для ускорения обработки данных
11. Из чего состоит Kafka кластер? #
Kafka-кластер — это распределенная система для передачи, хранения и обработки сообщений. Его основные компоненты:
| Компонент | Описание |
|---|---|
| Брокеры (Brokers) | Серверы, которые хранят данные и обрабатывают запросы производителей (producers) и потребителей (consumers). Каждый брокер имеет уникальный ID. |
| Топики (Topics) | Логические категории для сообщений. Сообщения внутри топика распределены по разделам (partitions). |
| Разделы (Partitions) | Логическое деление топика для масштабируемости. Сообщения в разделе упорядочены, между разделами порядок не гарантируется. |
| Реплики (Replicas) | Копии разделов, хранящиеся на других брокерах для отказоустойчивости. |
| Контроллер (Controller) | Один из брокеров, который управляет метаданными кластера и отвечает за перераспределение разделов в случае отказа. |
| Производители (Producers) | Клиенты, которые публикуют сообщения в топики. |
| Потребители (Consumers) | Клиенты, которые читают сообщения из топиков, обрабатывая данные в зависимости от задачи. |
| Consumer Group | Группа потребителей, которые совместно обрабатывают сообщения из одного топика, разделяя нагрузку. |
| Zookeeper / KRaft | Сервис координации для управления метаданными кластера и состоянием брокеров (Zookeeper заменяется на KRaft в новых версиях Kafka). |
12. Брокеры сообщений vs Rest #
| Характеристика | Брокеры сообщений (Kafka, RabbitMQ) | REST API |
|---|---|---|
| Модель коммуникации | Асинхронная (очереди, топики) | Синхронная (запрос-ответ) |
| Скорость передачи | Высокая, оптимизирована для работы с большими объемами данных | Зависят от производительности HTTP сервера |
| Надежность доставки | Поддерживает подтверждение (ACK), ретрансляцию сообщений | Обычно не предоставляет механизмов ретрансляции |
| Масштабируемость | Высокая, благодаря горизонтальному масштабированию брокеров | Требует отдельных решений для масштабирования |
| Обработка данных | Гибкая, подходит для потоковой обработки данных | Подходит для CRUD-операций |
| Сложность настройки | Требует установки и управления инфраструктурой | Легче развернуть, использует стандартный HTTP |
13. Pull и push модели, в чём разница? #
Pull-модель (”вытягивание”) - клиент запрашивает данные у сервера (или брокера). Пример - консьюмеры в Kafka запрашивают данные у брокеров Преимущества:
- клиент контролирует скорость обработки, что особенно важно при высокой нагрузке.
- легко обрабатывать данные на уровне клиента Недостатки:
- может возникнуть задержка, если клиент редко запрашивает данные
- требует постоянного запроса данных
Push (”проталкивание”) - сервер отправляет данные клиенту по мере их поступления. Пример: WebSocket, Firebase Cloud Messaging. Преимущества:
- меньшая задержка - данные сразу отправляются клиенту
- удобно для реального времени (например, уведомления) Недостатки:
- клиент может быть перегружен большим потоком данных
- контроль над потоком находится на сервере
14. Как гарантировать в Kafka идемпотентность, чтобы не было задвоений и потерь сообщений? #
Идемпотентность в Kafka означает, что одно и то же сообщение не будет записано несколько раз, даже если производитель (producer) повторно отправляет его из-за сбоев
Механизмы обеспечения идемпотентности:
- Предотвращение дублирования сообщений - включение идемпотентности у продюсера:
Параметр
enable.idempotenceв конфигурации продюсера позволяет Kafka отслеживать дубликаты сообщений с использованием уникального идентификатора производителя (Producer ID) и последовательного номера сообщения (Sequence Number)Properties props = new Properties(); props.put("enable.idempotence", "true"); - Предотвращение потерь:
- Ретрай на уровне продюсера - в конфигурации продюсера по умолчанию включена повторная отправка сообщений в случае отсутствия подтверждения от брокера (
acks) - Репликация и уровень подтверждений - установка
acksв"all"гарантирует запись сообщения во все ISR (InSync Replica) реплики
- Ретрай на уровне продюсера - в конфигурации продюсера по умолчанию включена повторная отправка сообщений в случае отсутствия подтверждения от брокера (
15. Как в Kafka гарантируется последовательность сообщений? #
Сообщения в одной партиции записываются последовательно. Kafka гарантирует, что потребитель получит их в том же порядке, в котором они были записаны. Kafka не гарантирует порядок сообщений между разными партициями топика, так как каждый раздел обрабатывается независимо
16. Что сделать, чтобы сообщения попадали в одну партицию? #
- Использовать одну партицию
- Использовать ключ партиционирования (key)
- Использовать кастомный метод партиционирования
- Реализовать собственный
Partitioner, чтобы задавать логику распределенияpublic class CustomPartitioner implements Partitioner { @Override public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) { // Кастомная логика выбора раздела return somePartitionIndex; } } - Зарегистрировать его в конфигурации:
spring: kafka: producer: properties: partitioner.class: org.apache.kafka.clients.producer.RoundRobinPartitioner
- Реализовать собственный
- Использовать прямое указание номера партиции при отправке
producer.send(new ProducerRecord<>("topic", 0, null, "value"));
17. Как работает Round Robin? #
Round Robin - это стратегия распределения данных, при которой сообщения равномерно отправляются в разные разделы топика
Логика:
- Производитель отправляет первое сообщение в первую доступную партицию
- Следующее сообщение отправляется в следующую партицию, и так далее
- После достижения последнего раздела цикл повторяется с первой партиции
Пример: Топик имеет 3 партиции:
Partition 0: Msg1, Msg4
Partition 1: Msg2, Msg5
Partition 2: Msg3, Msg6
18. Acknowledgment в Kafka #
Acknowledgment (acks) - это механизм подтверждения записи сообщения в Kafka. Производитель получает подтверждение от брокера после записи сообщения
Типы acks:
- acks = 0:
- производитель не ждет подтверждения
- самая быстрая, но ненадежная стратегия (возможна потеря сообщений)
- acks = 1:
- производитель ждет подтверждения от лидера-реплики
- сообщение считается доставленным, если лидер записал его в лог
- возможна потеря сообщений, если лидер сбоит до синхронизации реплик
- acks = all (или -1):
- производитель ждет подтверждения от всех реплик ISR, включая лидера
- максимальная надёжность - сообщения не теряются, если хотя бы одна реплика доступна
- подходит для критичных данных
19. Смещение (offset) в Kafka #
Offset - это уникальный идентификатор сообщения в рамках одного раздела. Он представляет собой смещение (индекс с 0) сообщения в логе брокера
Использование:
- потребители используют offset, чтобы отслеживать, какие сообщения уже были обработаны
- offset хранится в Kafka (по умолчанию в специальном топике
__consumer_offsets) или в клиенте
Виды управления:
- Автоматическое управление (auto commit) - Kafka сама обновляет offset после чтения сообщения, настраивается параметром
enable.auto.commit=true - Ручное управление (manual commit) - потребитель сам обновляет offset после успешной обработки сообщения, используется для большей надежности
consumer.commitSync(); // Обновление offset синхронно consumer.commitAsync(); // Обновление offset асинхронно
20. Повторное чтение данных из Kafka #
Kafka позволяет читать данные повторно, так как сообщения не удаляются сразу после прочтения
Как прочитать данные повторно?
- Сбросить offset у консьюмера - Kafka хранит offset (позицию в топике) каждого консьюмера, можно сбросить offset назад и прочитать сообщения повторно
kafka-consumer-groups --bootstrap-server localhost:9092 --group my-group --reset-offsets --to-earliest --execute - Использовать
enable.auto.commit=false- если offset не фиксируется автоматически, консьюмер будет читать данные заново при перезапускеspring: kafka: consumer: enable-auto-commit: true - Читать из с конкретного offset
consumer.seek( new TopicPartition("test-topic", 0), 42 );
21. DLQ topic? #
Dead Letter Queue (DLQ) - это отдельный Kafka-топик, куда отправляются сообщения с ошибками
Зачем нужен DLQ?
- Если консьюмер не может обработать сообщение (например, ошибка десериализации)
- Чтобы не блокировать обработку других сообщений
- Для повторной попытки обработки позже
Как настроить DLQ?
- Вручную отправлять сообщения в DLQ
try { processMessage(record); } catch (Exception e) { ProducerRecord<String, String> dlqRecord = new ProducerRecord<>("dlq-topic", record.key(), record.value()); kafkaProducer.send(dlqRecord); } - Использовать Kafka Connect DLQ - если используется Kafka Connect, можно настроить DLQ автоматически:
errors.deadletterqueue.topic.name=dlq-topic errors.deadletterqueue.context.headers.enable=true