Получить доступ

Kafka Consumer и consumer group

Группа и распределение партиций, poll-цикл, heartbeat и таймауты, модели commit offset, ребаланс и роль идемпотентности.

Consumer group и assignment

group.id задаёт логического подписчика. Брокер распределяет партиции темы между consumer'ами группы (assignment): в каждый момент одна партиция принадлежит ровно одному consumer'у группы. Так одна группа = work queue, разные группы = независимые pub/sub-потоки поверх того же лога.

Стратегия распределения настраивается: range и round-robin — классические; sticky и особенно cooperative-sticky стараются МИНИМИЗИРОВАТЬ движение партиций при ребалансе (важно для горячих кэшей). Если consumer'ов в группе больше, чем партиций, лишние простаивают без работы.

Партиции топика распределены между консьюмерами группы — по одному владельцу на партицию; при подключении нового консьюмера происходит ребаланс.
Каждую партицию читает ровно один консьюмер группы; при изменении состава происходит ребаланс.

Poll loop

Kafka — pull-модель: consumer сам ходит к брокеру через consumer.poll(timeout). Тот возвращает батч записей, consumer обрабатывает их и коммитит offset, после чего следующий poll(). Это единственный «нормальный» цикл — обработка и коммит в нём же.

Размером батча и поведением ожидания управляют параметры: max.poll.records (макс записей за один poll), fetch.min.bytes и fetch.max.wait.ms (брокер может подержать запрос, чтобы собрать больше данных — батчинг и на стороне брокера). Тяжёлый serializer/десериализация добавляют латентность каждого poll.

Heartbeat и обнаружение смерти

У consumer есть отдельный heartbeat-thread, который отправляет брокеру сигналы жизни. session.timeout.ms задаёт, через сколько без heartbeat consumer'а сочтут мёртвым и запустят rebalance.

Отдельно — max.poll.interval.ms: МАКСИМУМ между двумя poll(). Если обработка батча затянулась дольше, брокер считает consumer'а зависшим (даже если heartbeat шёл) и выгоняет его из группы. Отсюда правило: тяжёлую обработку либо ускоряют, либо повышают max.poll.interval.ms, либо выносят в отдельные воркеры, а consumer только декодирует и распределяет.

Модели commit offset

Способов фиксировать прогресс несколько. enable.auto.commit=true (фоновый авто-commit каждые auto.commit.interval.ms) проще, но опасен: легко закоммитить ДО обработки и потерять сообщения при падении. Для прода обычно отключают и коммитят руками.

Ручной commit: commitSync() — блокирующий и надёжный (повторяет ретраиваемые ошибки), commitAsync() — быстрее, но без гарантий. Практичный паттерн — обработать батч → commitSync после успеха (at-least-once + идемпотентность обработки). Чем коммитят: offset, который указывает на СЛЕДУЮЩЕЕ сообщение, а не последнее обработанное (классическая ошибка off-by-one).

Ребаланс

Ребаланс — перераспределение партиций между членами группы — случается при подключении/уходе consumer'а, изменении подписки или числа партиций. В классическом «eager» протоколе вся группа на короткое время ОСТАНАВЛИВАЕТСЯ (stop-the-world): все возвращают свои партиции, потом получают новые.

Cooperative rebalance (Kafka 2.4+, cooperative-sticky) делает то же ИНКРЕМЕНТАЛЬНО — двигаются только нужные партиции, остальные продолжают работу. Для cleanup и сохранения локального состояния перед сменой партиции вешают ConsumerRebalanceListener (commit, очистка кэша).

В Kafka 4.0 появился новый протокол групп (KIP-848, group.protocol=consumer): распределение партиций считает уже не лидер группы на клиенте, а координатор на брокере, и ребаланс идёт инкрементально по умолчанию — без остановки всей группы.

At-least-once и идемпотентность

Практический дефолт обработки — at-least-once: получили батч → обработали с побочным эффектом → закоммитили offset. Если упасть между обработкой и commit, при следующем запуске сообщение придёт повторно — отсюда требование к ИДЕМПОТЕНТНОЙ обработке (см. урок про основы).

Альтернатива — commit ДО обработки (at-most-once): дублей не будет, но при сбое сообщение потеряется. Для большинства бизнес-сценариев это неприемлемо, поэтому при-обработке-потом-commit + идемпотентность — устоявшаяся практика.

Как отвечать на собеседовании

Разберите группу и assignment (range/round-robin/sticky/cooperative-sticky; consumer ≤ партиций по полезной работе). Объясните pull-poll-цикл и его параметры (max.poll.records, fetch.min.bytes). Разделите session.timeout.ms (heartbeat) и max.poll.interval.ms (тяжёлая обработка → eviction). Сравните commit-модели (auto-commit опасен; ручной sync после успеха — дефолт) и порядок rebalance (eager STW vs cooperative-sticky без полного STW). Завершите тезисом at-least-once + идемпотентность как практическим стандартом.

Вопросы по теме

Читайте также

Закрытая бета

Знать тему — половина дела

На живом собеседовании sotto слышит вопрос интервьюера и показывает короткий ответ и план. Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем