Как устроена схема консюмер-продюсер в Kafka?
Что проверяют
Понимаете ли вы, что Kafka это распределённый журнал, а не очередь, из которой сообщение исчезает после чтения. Следом почти всегда спрашивают про группы консюмеров и про то, как не потерять и не обработать дважды сообщение, если воркер упал.
Как отвечать
Опишите путь сообщения от продюсера до консюмера:
- топик и партиции: топик делится на партиции, каждая партиция это упорядоченный журнал с растущим
offset. Порядок гарантирован только внутри партиции; - продюсер: выбирает партицию по хэшу ключа, поэтому события одного заказа с одним ключом попадают в одну партицию и читаются по порядку. Надёжность задаёт
acks: приacks=allзапись подтверждается, когда её получили все синхронные реплики; - брокеры и репликация: у каждой партиции есть лидер и реплики, по умолчанию запись и чтение идут через лидера. Метаданные кластера хранит KRaft, ZooKeeper в Kafka 4.0 убран совсем;
- группа консюмеров: каждая партиция достаётся ровно одному консюмеру группы. Шесть партиций и восемь консюмеров означают двух простаивающих. Когда участник входит в группу или падает, происходит ребалансировка;
- офсеты: группа коммитит прочитанную позицию во внутренний топик
__consumer_offsets, после рестарта чтение продолжается с неё. Сами сообщения живут до истеченияretention, чтение их не удаляет.
В Python обычно берут confluent-kafka (обёртка над librdkafka) или aiokafka для asyncio. Продюсер в confluent-kafka асинхронный: produce() кладёт запись в буфер, подтверждения приходят в колбэк при вызове poll(), а перед выходом нужен flush(). Для гарантии at-least-once выключают enable.auto.commit, коммитят офсет после обработки, а сам обработчик делают идемпотентным.
Где спотыкаются
Говорят, что Kafka гарантирует порядок по всему топику. Порядок есть только внутри партиции, и масштабирование через партиции оплачивается именно этим.
Оставляют автокоммит и удивляются потерям: офсет может закоммититься раньше, чем воркер обработал пачку и упал.
Добавляют консюмеров сверх числа партиций в надежде ускориться. Лишние экземпляры просто ждут, параллелизм группы ограничен количеством партиций.