Консьюмеры и группы: параллельное чтение

Урок про сторону чтения: как несколько консьюмеров делят работу, не наступая друг другу на ноги.

Группа консьюмеров (consumer group) — набор консьюмеров с общим group.id, между которыми Kafka распределяет партиции топика так, что каждую партицию читает ровно один член группы.

Зачем это нужно

Один консьюмер не справится с потоком в миллионы событий. Группа позволяет масштабировать чтение: запускаете N экземпляров одного сервиса с общим group.id, и Kafka сама раздаёт им партиции. Каждое событие обработает ровно один экземпляр — работа делится без дублирования.

Распределение партиций

  Топик 4 партиции, группа "billing" из 2 консьюмеров:

  C1 <-- P0, P1
  C2 <-- P2, P3

  Добавили C3:
  C1 <-- P0
  C2 <-- P1, P2
  C3 <-- P3
  (партиции перераспределились -- это ребаланс)

Разные группы читают топик независимо: у каждой свой набор оффсетов. Сервис аналитики и сервис биллинга с разными group.id оба прочитают все события — это broadcast между группами и распределение внутри группы.

Ребалансировка

Ребаланс — перераспределение партиций при изменении состава группы: консьюмер вошёл, вышел или «завис» и был исключён. Координатор группы (один из брокеров) переназначает партиции. На время ребаланса чтение приостанавливается — это его цена.

Пример конфигурации консьюмера

bootstrap.servers=localhost:9092
group.id=billing
key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
value.deserializer=org.apache.kafka.common.serialization.StringDeserializer
max.poll.records=500
session.timeout.ms=10000

Как работает под капотом

Каждый консьюмер шлёт координатору периодические heartbeat'ы. Если за session.timeout.ms heartbeat не пришёл, координатор считает консьюмера мёртвым и запускает ребаланс. Назначение партиций считает assignor (стратегия вроде range, round-robin или cooperative-sticky). Современная «кооперативная» ребалансировка отдаёт только часть партиций, не останавливая всю группу целиком (incremental cooperative rebalancing), — это резко снижает «паузы мира» по сравнению со старым подходом «отобрать всё и раздать заново».

Частые ошибки

  • Долгая обработка в poll-цикле. Если между вызовами poll проходит больше max.poll.interval.ms, консьюмера сочтут зависшим и исключат — ребаланс-шторм.
  • Больше консьюмеров, чем партиций. Лишние простаивают (см. урок про партиции).
  • Один group.id на несоединённые сервисы. Они начнут «воровать» партиции друг у друга, думая, что они одна группа.

Итоги

  • Внутри группы каждую партицию читает ровно один консьюмер — работа делится без дублей.
  • Разные группы читают топик независимо, со своими оффсетами (broadcast между группами).
  • Ребаланс перераспределяет партиции при смене состава; кооперативный режим снижает паузы.
Проверьте себя
1. Сколько консьюмеров одной группы читают конкретную партицию?
AВсе сразу
BРовно один
CМинимум два для надёжности
DНи одного
2. Как два сервиса с РАЗНЫМИ group.id читают один топик?
AДелят партиции между собой
BНезависимо: каждый прочитает все события, со своими оффсетами
CТолько один получит данные
DЭто вызывает ошибку
3. Что запускает ребалансировку?
AЛюбая запись продюсера
BИзменение состава группы: консьюмер вошёл, вышел или перестал слать heartbeat
CСжатие лога
DСоздание топика