Как распределяются сообщения между несколькими консьюмерами в Kafka?
Что проверяют
Различаете ли вы два уровня распределения: продюсер раскладывает сообщения по партициям, а группа делит партиции между консьюмерами. Следом обычно спрашивают, как сохранить порядок событий одного заказа и что будет, если добавить партиции.
Как отвечать
- продюсер: партицию выбирает клиент, а не брокер. С ключом это murmur2 от ключа по модулю числа партиций, поэтому события одного ключа попадают в одну партицию и читаются по порядку. Без ключа с версии 2.4 работает sticky-партиционирование: клиент заполняет батч для одной партиции и потом переключается, а не раскидывает сообщения по кругу поштучно;
- группа консьюмеров: каждая партиция в группе читается ровно одним консьюмером. Консьюмеров больше, чем партиций, значит лишние простаивают; меньше, значит кто-то читает несколько;
- несколько групп: каждая группа получает все сообщения топика независимо, так устроен pub/sub. Позиции групп хранятся в топике
__consumer_offsets; - ребаланс: при входе или выходе консьюмера партиции переназначаются, стратегию задаёт
partition.assignment.strategy. С Kafka 4.0 доступен новый протокол групп (KIP-848), где назначение считает брокер.
В Spring Kafka @KafkaListener(topics = "orders", groupId = "billing", concurrency = "3") поднимает три консьюмера внутри ConcurrentMessageListenerContainer. Ставить больше, чем партиций, бессмысленно. Если нужен параллелизм выше числа партиций, обработку внутри консьюмера выносят в пул с сохранением порядка по ключу, либо смотрят на share groups (KIP-932) из ветки 4.x, где одну партицию читают несколько консьюмеров как очередь.
Семантика по умолчанию «как минимум один раз»: после ребаланса часть сообщений может прийти повторно, обработчик должен это переживать.
Где спотыкаются
Говорят, что партицию выбирает брокер или что без ключа идёт чистый round-robin. Это устаревшая картина.
Добавляют партиции в топик с ключами и не учитывают, что остаток от деления изменится: новые события ключа уйдут в другую партицию, и порядок относительно старых сломается.
Путают группу и консьюмер: думают, что два экземпляра с одним groupId получат по копии сообщения, а они поделят поток между собой.