Что такое Dead Letter Queue (DLQ) и для чего она используется?

Ответ

Dead Letter Queue (DLQ) — это специальная очередь в системах обмена сообщениями (например, RabbitMQ, Kafka), предназначенная для хранения сообщений, которые не могут быть успешно обработаны основной очередью.

Основная цель — изолировать «проблемные» сообщения, чтобы они не блокировали обработку других сообщений и не терялись безвозвратно. Это позволяет разработчикам и системным администраторам анализировать ошибки и принимать решения о дальнейшей судьбе таких сообщений (например, исправить и переотправить или удалить).

Основные причины попадания сообщения в DLQ:

  1. Ошибка обработки: Обработчик (consumer) явно отклоняет сообщение (например, с помощью NACK в RabbitMQ), потому что не может его обработать (например, из-за неверного формата данных или временной недоступности внешнего сервиса).
  2. Истечение TTL (Time-To-Live): Сообщение находилось в очереди дольше установленного времени жизни.
  3. Превышение длины очереди: Основная очередь достигла максимального количества сообщений.

Как это работает (на примере RabbitMQ):

  1. Создается основная очередь (work-queue) и настраивается так, чтобы при невозможности обработки она перенаправляла сообщения в специальный обменник (dlx-exchange).
  2. Создается очередь для «мертвых» писем (dlq-queue).
  3. dlq-queue привязывается к dlx-exchange.

Пример обработчика на Go, который может отправить сообщение в DLQ:

// ... код подключения к RabbitMQ ...

msgs, err := ch.Consume(
    "work-queue", // Название основной очереди
    "",           // consumer
    false,        // auto-ack = false, ручное подтверждение
    false,
    false,
    false,
    nil,
)

for msg := range msgs {
    log.Printf("Получено сообщение: %s", msg.Body)

    // Пытаемся обработать сообщение
    if err := processMessage(msg.Body); err != nil {
        log.Printf("Ошибка обработки: %v. Отправляем в DLQ.", err)
        // Отклоняем сообщение (requeue=false), чтобы оно ушло в DLQ,
        // а не вернулось в исходную очередь.
        msg.Nack(false, false)
    } else {
        // Сообщение успешно обработано
        msg.Ack(false)
    }
}