Брокеры сообщений #
1. Плюсы и минусы брокеров сообщений #
Плюсы:
- Асинхронность: Службы могут работать независимо друг от друга.
- Масштабируемость: Легко увеличивать количество потребителей.
- Надежность: Сообщения не теряются благодаря механизмам повторных попыток и подтверждений.
- Разделение нагрузки: Потребители могут обрабатывать сообщения параллельно.
Минусы:
- Сложность настройки: Требует правильной конфигурации для эффективной работы.
- Задержка: Добавление брокера увеличивает задержку доставки сообщений.
- Поддержка инфраструктуры: Нужны усилия для мониторинга и администрирования.
2. Разница между Kafka и RabbitMQ #
Модели сообщений
- Kafka. Сообщения не удаляются после доставки
- RabbitMQ. Сообщения удаляются после доставки
Обработка сообщений
- Kafka. Потребители сами запрашивают сообщения из топиков, читая их по мере необходимости
- RabbitMQ. Сообщения пушатся к потребителям из очереди, когда те готовы их принять (следовательно сервер может лечь, если данных данных слишком много)
Производительность
- Kafka быстрее и производительнее чем RabbitMQ (за счет partition-ов + горизонтальное масштабирование)
3. Что такое топик в Kafka? #
Топик - это канал передачи информации определенного типа. Каждый топик в свою очередь разделен на partition-ы
На что делится топик?
В Apache Kafka топик (topic) делится на разделы (partitions). Разделение топика на разделы позволяет распределять сообщения между брокерами, обрабатывать их параллельно и обеспечивать масштабируемость и отказоустойчивость.
4. Что такое Producer и что такое Consumer? #
- Producer - компонент, отправляющий сообщения (данные) в Kafka
- Consumer - компонент, получающий сообщения из Kafka
Что даст увеличение количества консьюмеров?
Увеличение количества потребителей (consumers) в группе потребителей (consumer group) может повлиять на производительность и обработку данных, но с определенными ограничениями:
Параллельная обработка:
- Увеличение числа потребителей в группе позволяет обрабатывать данные параллельно, так как каждый потребитель обрабатывает свои разделы (partitions).
- Если количество потребителей меньше или равно количеству разделов, это приводит к увеличению производительности.
Ограничения:
- Один раздел может быть назначен только одному потребителю в группе.
- Если количество потребителей превышает количество разделов, “лишние” потребители не будут получать данные, так как разделы не могут быть разделены между несколькими потребителями.
Пример:
- Топик с 4 разделами:
- Если в группе 2 потребителя, то каждый будет обрабатывать 2 раздела.
- Если в группе 4 потребителя, каждый получит 1 раздел.
- Если в группе 5 потребителей, один из них останется без работы.
- Топик с 4 разделами:
5. Что такое partition в Kafka? #
Это подразделение топика, в котором хранятся сообщения. По default-у сообщения будут закидываться в разные partition-ы, что увеличивает скорость обмена сообщениями между микросервисами, но при этом нет гарантии очередности доставки сообщений. Если нам надо, что бы какие-то сообщения приходили в строгой очередности - для этого существует ключ partition-ов, который позволяет объединить сообщения в очередь в конкретном partition-е
На что делится partition?
Раздел (partition) не делится дальше, он является минимальной единицей хранения и обработки данных в Kafka. Однако внутри раздела данные организуются следующим образом:
Лог записи:
- Сообщения в разделе хранятся в лог-файле и имеют уникальные смещения (offsets), которые используются для идентификации и упорядочивания сообщений.
Реплики:
- Каждый раздел может иметь одну или несколько копий (реплик) для обеспечения отказоустойчивости.
Лидер и фолловеры:
- Один брокер управляет разделом как лидер, а остальные брокеры хранят реплики и выступают как фолловеры.
Как сообщения распределяются по партициям?
Производитель (producer) определяет, в какой раздел отправить сообщение, используя следующие подходы:
Без ключа (Round-Robin):
Если ключ не указан, сообщения равномерно распределяются по всем доступным разделам.
Пример:
Partition 0: Msg1, Msg4 Partition 1: Msg2, Msg5 Partition 2: Msg3, Msg6
С использованием ключа (Key-based partitioning):
Если сообщение содержит ключ, Kafka использует хеширование ключа для выбора раздела:
partition = hash(key) % number_of_partitions
Это гарантирует, что все сообщения с одинаковым ключом будут попадать в один раздел.
Кастомный алгоритм (Custom Partitioning):
- Производитель может реализовать собственную логику распределения сообщений по разделам, например, на основе содержимого сообщения.
Пример с ключами:
Топик с 3 разделами:
Key: "user1" -> Partition 0 Key: "user2" -> Partition 1 Key: "user3" -> Partition 2
Manual Partitioning:
- Производитель может явно указать, в какой раздел отправить сообщение.
6. Гарантии доставки в Kafka #
- At least once. Гарантия того, что сообщение будет доставлено по крайней мере один раз, либо больше.
- At most once. Гарантия того, что сообщение будет доставлено максимум один раз, либо не будет доставлено вовсе
- Exactly once. Гарантия того, что сообщение будет доставлено ровно один раз. Это наиболее строгий уровень гарантии и требует согласованности между producer-ом и consumer-ом, чтобы избежать дублирования сообщений
7. Какие существуют паттерны работы с очередями? #
- Point-to-Point (P2P): Один продюсер, один потребитель. Сообщение читается одним потребителем и удаляется из очереди.
- Publish-Subscribe (Pub/Sub): Один продюсер отправляет сообщение нескольким подписчикам через обменники.
- Work Queue: Несколько потребителей обрабатывают сообщения из одной очереди, распределяя нагрузку.
- Dead Letter Queue (DLQ): Очередь для сообщений, которые не удалось обработать.
- Priority Queue: Сообщения обрабатываются в зависимости от их приоритета.
8. Основные компоненты Kafka? #
Apache Kafka — это распределённая система обработки потоков, состоящая из нескольких ключевых компонентов:
- Producer (Производитель) — отправляет данные в Kafka.
- Consumer (Потребитель) — получает данные из Kafka.
- Event (Событие) - это основная единица данных, которая передается и хранится в системе. Состоит из ключа (опционально), значения, заголовков и метки времени.
- Topics (Топики) — логическая сущность, разделяющая данные на каналы. Каждый топик хранит сообщения в порядке их поступления.
- Partitions (Партиции) — подмножества топиков, которые разделяют данные для их распределенного хранения и параллельной обработки. Партиции дают возможность хранить данные на разных серверах.
- Broker (Брокер) — сервер, ответственный за хранение данных в Kafka.
- ZooKeeper — служит для управления и координации брокеров, поддерживает метаданные системы. Однако в новых версиях Kafka можно использовать KRaft (Kafka Raft), который заменяет ZooKeeper.
- Consumer Group (Группа потребителей) — объединение консьюмеров, которые параллельно обрабатывают данные одного топика. Каждый консьюмер в группе обрабатывает разные партиции топика, что улучшает параллельность и производительность.
9. Kafka. Consumer Group #
Consumer Group — это механизм для группировки консьюмеров, работающих с одним топиком, для:
- Распределения нагрузки: каждый консьюмер из группы читает данные только из одной или нескольких партиций, избегая дублирования. Например, если у нас три партиции и три консьюмера в группе, каждый из них будет работать с одной партицией.
- Гибкости: можно масштабировать количество консьюмеров в группе для более быстрой обработки данных.
Консьюмеры внутри группы координируются так, что каждое сообщение обрабатывается ровно одним консьюмером в группе, что обеспечивает гарантию «один раз» для сообщений.
10. Из чего состоит Kafka кластер? #
Kafka-кластер — это распределенная система для передачи, хранения и обработки сообщений. Его основные компоненты:
Компонент | Описание |
---|---|
Брокеры (Brokers) | Серверы, которые хранят данные и обрабатывают запросы производителей (producers) и потребителей (consumers). Каждый брокер имеет уникальный ID. |
Топики (Topics) | Логические категории для сообщений. Сообщения внутри топика распределены по разделам (partitions). |
Разделы (Partitions) | Логическое деление топика для масштабируемости. Сообщения в разделе упорядочены, между разделами порядок не гарантируется. |
Реплики (Replicas) | Копии разделов, хранящиеся на других брокерах для отказоустойчивости. |
Контроллер (Controller) | Один из брокеров, который управляет метаданными кластера и отвечает за перераспределение разделов в случае отказа. |
Производители (Producers) | Клиенты, которые публикуют сообщения в топики. |
Потребители (Consumers) | Клиенты, которые читают сообщения из топиков, обрабатывая данные в зависимости от задачи. |
Consumer Group | Группа потребителей, которые совместно обрабатывают сообщения из одного топика, разделяя нагрузку. |
Zookeeper / KRaft | Сервис координации для управления метаданными кластера и состоянием брокеров (Zookeeper заменяется на KRaft в новых версиях Kafka). |
11. Брокеры сообщений vs Rest #
Характеристика | Брокеры сообщений (Kafka, RabbitMQ) | REST API |
---|---|---|
Модель коммуникации | Асинхронная (очереди, топики) | Синхронная (запрос-ответ) |
Скорость передачи | Высокая, оптимизирована для работы с большими объемами данных | Зависят от производительности HTTP сервера |
Надежность доставки | Поддерживает подтверждение (ACK), ретрансляцию сообщений | Обычно не предоставляет механизмов ретрансляции |
Масштабируемость | Высокая, благодаря горизонтальному масштабированию брокеров | Требует отдельных решений для масштабирования |
Обработка данных | Гибкая, подходит для потоковой обработки данных | Подходит для CRUD-операций |
Сложность настройки | Требует установки и управления инфраструктурой | Легче развернуть, использует стандартный HTTP |
12. Pull и push модели, в чём разница? #
Pull (вытягивание):
- Описание:
- Клиент запрашивает данные у сервера (или брокера).
- Пример: Kafka, где потребители сами определяют, когда забирать данные.
- Преимущества:
- Клиент контролирует скорость обработки.
- Легко обрабатывать данные на уровне клиента.
- Недостатки:
- Может возникнуть задержка, если клиент редко запрашивает данные.
- Требует постоянного запроса данных.
Push (запихивание):
- Описание:
- Сервер отправляет данные клиенту по мере их поступления.
- Пример: WebSocket, Firebase Cloud Messaging.
- Преимущества:
- Меньшая задержка: данные сразу отправляются клиенту.
- Удобно для реального времени (например, уведомления).
- Недостатки:
- Клиент может быть перегружен большим потоком данных.
- Контроль над потоком находится на сервере.
Kafka и модели:
- Kafka использует Pull модель:
- Потребители (Consumers) сами запрашивают данные у брокеров.
- Это позволяет потребителям контролировать обработку данных, что особенно важно при высокой нагрузке.
13. Как гарантировать в Kafka идемпотентность, чтобы не было задвоений и потерь сообщений? #
Идемпотентность в Kafka означает, что одно и то же сообщение не будет записано несколько раз, даже если производитель (producer) повторно отправляет его из-за сбоев или переотправок.
Механизмы обеспечения идемпотентности:
Включение идемпотентности у производителя:
Включите параметр
enable.idempotence
в конфигурации продюсера:Properties props = new Properties(); props.put("enable.idempotence", "true");
Это позволяет Kafka отслеживать дубликаты сообщений с использованием уникального идентификатора производителя (Producer ID) и последовательного номера сообщения (Sequence Number).
Ретрай на уровне продюсера:
Используйте конфигурацию
retries
для автоматической повторной отправки сообщений в случае временных ошибок:props.put("retries", Integer.MAX_VALUE);
Репликация:
Убедитесь, что уровень подтверждений (
acks
) установлен в"all"
, чтобы гарантировать запись сообщения во все реплики:props.put("acks", "all");
Idempotent Producer:
- При включенной идемпотентности Kafka автоматически предотвращает дублирование сообщений на уровне брокера.
Идемпотентность и транзакции:
Используйте транзакционные сообщения, если нужно обеспечить атомарность для группы операций:
props.put("transactional.id", "unique-transaction-id"); producer.initTransactions(); producer.beginTransaction(); producer.send(...); producer.commitTransaction();
14. Как в Kafka гарантируется последовательность сообщений? #
Последовательность в рамках одного раздела (partition):
Порядок внутри раздела:
- Сообщения в одном разделе записываются последовательно. Kafka гарантирует, что потребитель получит их в том же порядке, в котором они были записаны.
Производитель с одним соединением:
- Чтобы сохранить порядок сообщений, производитель должен использовать одно соединение для записи в раздел.
Подтверждения (
acks
):- Используйте
acks=all
для гарантии, что сообщение записано всеми репликами перед подтверждением.
- Используйте
Идемпотентность:
- Включение идемпотентности предотвращает нарушение порядка из-за повторной отправки сообщений.
Последовательность между разделами:
- Kafka не гарантирует порядок сообщений между разными разделами топика, так как каждый раздел обрабатывается независимо.
15. Что сделать, чтобы сообщения попадали в одну партицию? #
Чтобы все сообщения с определенными характеристиками направлялись в одну партицию:
Использование ключа (Key):
Укажите ключ сообщения (
key
), чтобы Kafka использовала его для хеширования и выбора раздела:producer.send(new ProducerRecord<>("topic", "key", "value"));
Сообщения с одинаковым ключом всегда будут направляться в один и тот же раздел.
Настройка кастомного партишенера:
Реализуйте собственный
Partitioner
, чтобы задавать логику распределения:public class CustomPartitioner implements Partitioner { @Override public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) { // Кастомная логика выбора раздела return somePartitionIndex; } }
Зарегистрируйте кастомный партишенер в конфигурации:
props.put("partitioner.class", "com.example.CustomPartitioner");
Выбор раздела вручную:
Укажите номер раздела вручную при отправке сообщения:
producer.send(new ProducerRecord<>("topic", 0, null, "value"));
Здесь
0
— номер раздела.
16. Как работает Round Robin? #
Round Robin — это стратегия распределения данных, при которой сообщения равномерно отправляются в разные разделы топика.
Механизм работы:
Когда используется:
- Производитель не задает ключ (
key
) при отправке сообщения. - Используется стандартный
DefaultPartitioner
.
- Производитель не задает ключ (
Логика:
- Производитель отправляет первое сообщение в первый доступный раздел.
- Следующее сообщение отправляется в следующий раздел, и так далее.
- После достижения последнего раздела цикл повторяется с первого раздела.
Пример:
Топик имеет 3 раздела:
Partition 0: Msg1, Msg4 Partition 1: Msg2, Msg5 Partition 2: Msg3, Msg6
Особенности:
- Round Robin обеспечивает равномерное распределение сообщений между всеми разделами.
- Порядок сообщений не гарантируется между разделами.
- Если разделы имеют разные лидеры, производитель сначала выбирает лидера для распределения нагрузки.
17. Acknowledgment в Kafka #
Acknowledgment (acks) — это механизм подтверждения записи сообщения в Kafka. Производитель получает подтверждение от брокера после записи сообщения.
Типы acks:
acks = 0:
- Производитель не ждет подтверждения.
- Самая быстрая, но ненадежная стратегия (возможна потеря сообщений).
acks = 1:
- Производитель ждет подтверждения от лидера раздела.
- Сообщение считается доставленным, если лидер записал его в лог.
- Возможна потеря сообщений, если лидер сбоит до синхронизации реплик.
acks = all (или -1):
- Производитель ждет подтверждения от всех реплик, включая лидера.
- Максимальная надежность: сообщения не теряются, если хотя бы одна реплика доступна.
- Подходит для критичных данных.
Пример настройки acks:
Properties props = new Properties();
props.put("acks", "all");
18. Смещение (offset) в Kafka #
Offset — это уникальный идентификатор сообщения в рамках одного раздела. Он представляет собой смещение (номер) сообщения в логе.
Как работает offset:
Уникальность:
- Каждое сообщение в разделе имеет уникальный offset, начиная с
0
.
- Каждое сообщение в разделе имеет уникальный offset, начиная с
Использование:
- Потребители используют offset, чтобы отслеживать, какие сообщения уже были обработаны.
- Offset хранится в Kafka (по умолчанию в специальном топике
__consumer_offsets
) или в клиенте.
Автоматическое и ручное управление:
Автоматическое управление (auto commit):
- Kafka сама обновляет offset после чтения сообщения.
- Настраивается параметром
enable.auto.commit=true
.
Ручное управление (manual commit):
- Потребитель сам обновляет offset после успешной обработки сообщения.
- Используется для большей надежности.
consumer.commitSync(); // Обновление offset синхронно
Пример работы offset:
Топик с 2 разделами:
Partition 0: Msg1 (offset=0), Msg2 (offset=1) Partition 1: Msg3 (offset=0), Msg4 (offset=1)
Потребитель может обрабатывать сообщения в любом порядке, используя offset как точку отсчета.
19. Повторное чтение данных из Kafka #
Kafka позволяет читать данные повторно, так как сообщения не удаляются сразу после потребления.
📌 Как прочитать данные повторно?
Изменить offset у Consumer’а
- Kafka хранит offset (позицию в топике) каждого Consumer’а.
- Можно сбросить offset назад и прочитать сообщения повторно.
📌 Пример:
kafka-consumer-groups --bootstrap-server localhost:9092 --group my-group --reset-offsets --to-earliest --execute
✔ Устанавливает offset на самое начало (
--to-earliest
).Использовать
enable.auto.commit=false
- Если offset не фиксируется, Consumer будет читать данные заново при перезапуске.
📌 Пример конфигурации:
Properties props = new Properties(); props.put("enable.auto.commit", "false"); // Offset не фиксируется автоматически
Читать из
__consumer_offsets
- Kafka хранит offset’ы в системном топике
__consumer_offsets
. - Можно узнать текущий offset и переустановить его.
- Kafka хранит offset’ы в системном топике
20. DLQ topic? #
Dead Letter Queue (DLQ) – это отдельный Kafka-топик, куда отправляются сообщения с ошибками.
📌 Зачем нужен DLQ?
- Если Consumer не может обработать сообщение (например, ошибка десериализации).
- Чтобы не блокировать обработку других сообщений.
- Для повторной попытки обработки позже.
📌 Как настроить DLQ?
1️⃣ Вручную отправлять сообщения в DLQ
📌 Пример на Java:
try {
processMessage(record);
} catch (Exception e) {
ProducerRecord<String, String> dlqRecord =
new ProducerRecord<>("dlq-topic", record.key(), record.value());
kafkaProducer.send(dlqRecord);
}
✔ Если сообщение не обработалось, оно отправляется в DLQ-топик.
2️⃣ Использовать Kafka Connect DLQ
Если используется Kafka Connect, можно настроить DLQ автоматически:
errors.deadletterqueue.topic.name=dlq-topic
errors.deadletterqueue.context.headers.enable=true
✔ Сообщения с ошибками будут автоматически отправляться в DLQ.