Какие механизмы в Kafka используются для управления смещениями (offsets) и предотвращения повторной обработки сообщений?

Ответ

Для гарантии доставки и предотвращения дублирования в Kafka используется механизм управления смещениями (offset management). Каждая группа потребителей (consumer group) отслеживает свою позицию (offset) в каждой партиции топика.

Существует три основных семантики доставки:

  • At-most-once (не более одного раза): Смещение коммитится до обработки сообщения. При сбое сообщение будет потеряно.
  • At-least-once (как минимум один раз): Смещение коммитится после обработки. При сбое до коммита сообщение может быть обработано повторно. Это самый частый сценарий.
  • Exactly-once (строго один раз): Гарантирует, что сообщение обработано ровно один раз, даже при сбоях. Достигается с помощью транзакций и идемпотентного продюсера.

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


  1. Автоматический коммит (enable.auto.commit=true): Брокер периодически коммитит смещение за потребителя. Просто, но рискованно — возможны как потери, так и дубликаты.



  2. Ручной коммит (enable.auto.commit=false): Потребитель явно подтверждает обработку. Это надежный способ для реализации at-least-once.


Пример ручного коммита в Go с использованием sarama (современный подход с ConsumerGroup):

// ConsumerGroupHandler реализует логику обработки

type handler struct{}

func (h *handler) Setup(_ sarama.ConsumerGroupSession) error   { return nil }
func (h *handler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
func (h *handler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
    for msg := range claim.Messages() {
        // 1. Обрабатываем сообщение
        processMessage(msg)

        // 2. Явно коммитим offset
        sess.MarkMessage(msg, "")
    }
    return nil
}

Для строгой гарантии exactly-once необходимо использовать Transactional API в Kafka, что требует более сложной координации между продюсером и консьюмером.