Ответ
Kafka гарантирует доставку сообщения от брокера к консьюмеру (at-least-once), но не может знать, была ли бизнес-логика консьюмера выполнена успешно. Ответственность за подтверждение обработки лежит на самом консьюмере. Это делается через механизм коммита смещений (offset commit).
Offset — это, по сути, закладка, которая указывает, какое сообщение в партиции топика было обработано последним.
Способы управления коммитами:
-
Автоматический коммит (Auto Commit)
- Как работает: Консьюмер автоматически коммитит офсеты через заданный интервал (
auto.commit.interval.ms). Это поведение включено по умолчанию во многих библиотеках (enable.auto.commit=true). - Риски: Это самый простой, но и самый ненадежный способ. Если приложение упадет после коммита, но до завершения обработки сообщения, сообщение будет потеряно.
- Как работает: Консьюмер автоматически коммитит офсеты через заданный интервал (
-
Ручной коммит (Manual Commit) — Рекомендуемый подход
- Как работает: Вы отключаете автокоммит (
enable.auto.commit=false) и явно вызываете методCommit()после успешной обработки сообщения. Это дает полный контроль над процессом. - Гарантии: Обеспечивает семантику "at-least-once" (как минимум один раз). Если приложение упадет после обработки, но до коммита, то после перезапуска оно снова получит это же сообщение.
// Псевдокод на примере библиотеки confluent-kafka-go for { msg, err := consumer.ReadMessage(-1) if err != nil { // Обработка ошибки continue } // 1. Ваша бизнес-логика по обработке сообщения processMessage(msg.Value) // 2. Если обработка прошла успешно, коммитим офсет // Этот вызов сообщает Kafka: "Я успешно обработал это сообщение и все предыдущие" _, err = consumer.CommitMessage(msg) if err != nil { log.Printf("Failed to commit offset: %v", err) } } - Как работает: Вы отключаете автокоммит (
Альтернативные паттерны:
-
Использование транзакций (Exactly-Once Semantics): В сценариях "прочитать-обработать-записать" (consume-process-produce) можно использовать транзакции Kafka. Они позволяют атомарно закоммитить офсет прочитанного сообщения и записать результат обработки в другой топик. Либо вся операция проходит успешно, либо ничего не происходит.
-
Отдельный топик для подтверждений (Ack Topic): Консьюмер после обработки отправляет сообщение-подтверждение в специальный
ack-topic. Другой сервис может слушать этот топик, чтобы отслеживать статус обработки. Этот паттерн усложняет архитектуру, но добавляет гибкости.
Ответ 18+ 🔞
А, слушай-ка, вот тут у нас классическая засада с этой вашей Кафкой! Она-то, сука, как почтальон Печкин — письмо в ящик сунул и пошёл себе, довольный. Доставил, блядь, и ладно! А что ты с этим письмом сделал — прочитал, в сортире подтёрся или вообще не открывал — ей, блядь, похуй!
Вот в этом и весь прикол. Кафка гарантирует, что сообщение от брокера до консьюмера доползёт (хотя бы разок), но вот обработал ли ты его — это уже твои, блядь, проблемы, дружок. Ответственность за этот пиздец целиком на тебе, через эту штуку — коммит смещений (offset commit).
Offset — это типа закладка в книжке. Показал Кафке: "Я вот до этой страницы прочитал, всё ок". А дальше она тебе следующую даёт.
Ну и как этим хозяйством управлять? Вариантов — как говна за баней.
-
Автокоммит (Auto Commit) — для ленивых и безбашенных
- Суть: Консьюмер сам, по таймеру (
auto.commit.interval.ms), херачит коммит. Во многих библиотеках это и так включено (enable.auto.commit=true). - Подводный ебальник: Способ — пиздец какой простой, но и опасный, как хуй в розетке. Представь: он закоммитил офсет, а твоё приложение через секунду накрылось медным тазом, так и не доделав работу. И всё, пиши пропало! Сообщение считается обработанным и больше никогда не придёт. Потерял, сука, нахуй.
- Суть: Консьюмер сам, по таймеру (
-
Ручной коммит (Manual Commit) — для адекватных мужиков (рекомендуется!)
- Суть: Вырубаешь автокоммит нахуй (
enable.auto.commit=false) и сам, своими ручками, вызываешьCommit()только после того, как вся твоя бизнес-логика отработала без сучка и задоринки. - Что даёт: Жёсткую гарантию "at-least-once" (как минимум один раз). Упало приложение после обработки, но до коммита? Ничего страшного, поднимется — получит то же самое сообщение ещё разок. Можешь обработать его дважды, зато не потеряешь, епта!
// Примерный код, суть уловишь for { // Получаем сообщение msg, err := consumer.ReadMessage(-1) if err != nil { // Ну тут ошибка, с ней разбирайся continue } // 1. САМОЕ ГЛАВНОЕ: выполняем нашу ебучую бизнес-логику // (запись в БД, вызов API, расчёт ядер чистого изумруда) processMessage(msg.Value) // 2. Только если ВСЁ прошло успешно — коммитим! // Кричим Кафке: "Я, блядь, справился! Дай следующее!" _, err = consumer.CommitMessage(msg) if err != nil { log.Printf("Ой, бля, не закоммитилось: %v", err) } } - Суть: Вырубаешь автокоммит нахуй (
А есть ещё, блядь, извращения для самых хитрых:
-
Транзакции (Exactly-Once Semantics): Это когда ты в одном, блядь, атомарном действии и сообщение обрабатываешь, и результат в другой топик пишешь, и офсет коммитишь. Либо всё срастается, либо — чих-пых тебя в сраку — откатывается как не бывало. Сложно, но для финансовых расчётов — то, что надо.
-
Отдельный топик для подтверждений (Ack Topic): Во, это вообще высший пилотаж, ёперный театр! Обработал сообщение — шлёшь бумажку "Всё ок" в специальный
ack-topic. А там уже другой сервис сидит, отслеживает эту всю движуху. Архитектура, блядь, усложняется в овердохуища раз, но гибкость — хоть жопой ешь.
Короче, вывод простой, как три рубля: не доверяй автокоммиту, если данные хоть чуть-чуть ценные. Бери управление в свои руки и коммить вручную, после успешной работы. А то будет тебе, как Герасиму с Муму — совесть потом загложет, что сообщение-то утопил, сука.