Как обеспечивается разделение сдвигов у реплицированных Kafka Consumer в микросервисах
Что проверяют
Понимаете ли вы, как несколько реплик одного сервиса делят топик и почему они не читают одно и то же. Главное слово здесь партиция, а не сообщение. Следом спрашивают, что происходит при падении реплики и откуда берутся дубли.
Как отвечать
Все реплики сервиса запускаются с одинаковым group.id и образуют одну consumer group. Kafka делит между ними не сообщения, а партиции: каждая партиция в группе закреплена ровно за одним потребителем. Отсюда два следствия. Если реплик больше, чем партиций, лишние простаивают. Порядок гарантирован только внутри партиции, поэтому связанные события отправляют с одним ключом.
Сдвиги хранятся не у потребителя, а в брокере, во внутреннем топике __consumer_offsets, по ключу «группа, топик, партиция». Если реплика упала, после ребалансировки её партиции читают соседи с последнего закоммиченного сдвига.
Что стоит назвать:
- коммит: у голого
KafkaConsumerпо умолчаниюenable.auto.commit=trueс интервалом 5 секунд, что даёт и потери, и дубли. Spring Kafka выключает автокоммит и сам коммитит после обработки пачки (AckMode.BATCH), для точного контроля естьMANUAL_IMMEDIATE; - ребалансировка:
CooperativeStickyAssignorне останавливает всю группу, а статическое членство черезgroup.instance.idизбавляет от ребалансировки при коротком рестарте пода. С Kafka 4.0 доступен новый протокол групп (KIP-848), где назначение партиций считает брокер; - разные сервисы: разные
group.id, и каждый независимо получает все сообщения топика.
Доставка остаётся at-least-once: реплика может обработать пачку и упасть до коммита. Поэтому обработчик делают идемпотентным, например с таблицей обработанных id.
Где спотыкаются
Думают, что сообщения раздаются по одному по кругу. Отсюда удивление, почему при шести репликах и трёх партициях половина подов ничего не делает.
Генерируют group.id из имени пода. Каждая реплика становится отдельной группой и читает весь топик, всё обрабатывается многократно.
Уверены, что группа сама исключает дубли. Если обработка пачки дольше max.poll.interval.ms, потребителя выкидывают из группы, и незакоммиченные сообщения достаются другому.