Как в Kafka убедиться, что сообщение было успешно обработано консьюмером?

Ответ

Kafka гарантирует доставку сообщения от брокера к консьюмеру (at-least-once), но не может знать, была ли бизнес-логика консьюмера выполнена успешно. Ответственность за подтверждение обработки лежит на самом консьюмере. Это делается через механизм коммита смещений (offset commit).

Offset — это, по сути, закладка, которая указывает, какое сообщение в партиции топика было обработано последним.

Способы управления коммитами:

  1. Автоматический коммит (Auto Commit)

    • Как работает: Консьюмер автоматически коммитит офсеты через заданный интервал (auto.commit.interval.ms). Это поведение включено по умолчанию во многих библиотеках (enable.auto.commit=true).
    • Риски: Это самый простой, но и самый ненадежный способ. Если приложение упадет после коммита, но до завершения обработки сообщения, сообщение будет потеряно.
  2. Ручной коммит (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. Другой сервис может слушать этот топик, чтобы отслеживать статус обработки. Этот паттерн усложняет архитектуру, но добавляет гибкости.