Прямой ответ
Очередь недоставленных сообщений (dead letter queue, DLQ) — место, куда попадают сообщения, которые обработчик не смог обработать. Без неё такое сообщение либо теряется, либо блокирует очередь повторными попытками.
Причины попадания в DLQ:
| Причина | Пример | Что делать |
|---|---|---|
| Отравленное сообщение | Неверный формат тела | Исправить данные либо отбросить |
| Исчерпан предел повторов | Внешний сервис недоступен | Повторить позже |
| Истёк срок жизни | Сообщение устарело | Отбросить после проверки |
| Ошибка обработчика | Исключение в коде | Исправить код и повторить |
| Отклонено получателем | Неверная маршрутизация | Исправить правила маршрутизации |
Основное различие — временная ошибка и постоянная. Временная ошибка (недоступность внешнего сервиса) разрешается повтором. Постоянная (неверный формат данных) не разрешается никогда, и повторная обработка расходует ресурсы без результата.
Порядок обработки с пределом повторов:
MAX_RETRIES = 5
def handle(message):
try:
process(message)
except TransientError:
if message.retry_count < MAX_RETRIES:
republish_with_delay(message) # повтор с нарастающей задержкой
else:
publish_to_dlq(message, reason="retries_exhausted")
except PermanentError as e:
publish_to_dlq(message, reason=str(e))
Нарастающая задержка между повторами важна: немедленный повтор при недоступном сервисе создаёт поток запросов к уже перегруженной системе; о механизме — в статье про повторы и circuit breaker.
Отравленное сообщение — частный случай постоянной ошибки. Сообщение, вызывающее исключение при разборе, будет вызывать его всегда, и без DLQ оно блокирует очередь, поскольку обработчик не может его ни обработать, ни пропустить; о гарантиях доставки — в статье про надёжность доставки.
Разбор сообщений в DLQ — отдельный процесс, а не автоматическая операция. Сообщение хранит причину попадания, исходные данные и число попыток.
Разбор сообщений в DLQ
Очередь недоставленных сообщений — не хранилище забытых данных, а очередь на разбор. Без процедуры она превращается в архив невыполненных операций.
Шаги разбора:
1. Определить причину по сохранённой метке.
2. Классифицировать: ошибка данных, ошибка кода, внешний сбой.
3. Для ошибки данных — исправить источник и вернуть сообщение.
4. Для ошибки кода — исправить обработчик, затем вернуть сообщения.
5. Для внешнего сбоя — дождаться восстановления и вернуть пакетом.
6. Зафиксировать результат разбора.
Классификация определяет действие. Ошибка данных требует исправления на входе, иначе те же сообщения появятся снова. Ошибка кода требует исправления обработчика до возврата, иначе возврат воспроизведёт сбой. Внешний сбой требует только ожидания.
Повтор выполняется пакетом и контролируемо. Возврат тысячи сообщений одновременно создаёт пик нагрузки на систему, которая только что восстановилась. Повтор выполняется дозированно.
Проверка идемпотентности обязательна. Сообщение могло быть обработано частично перед ошибкой, и повтор выполнит часть действий дважды.
Срок хранения ограничивает объём. Сообщения хранятся достаточно долго для разбора, обычно от нескольких дней до месяца, после чего удаляются с фиксацией факта удаления.
Отсутствие процедуры разбора приводит к двум исходам: сообщения копятся и операции не выполняются, либо очередь периодически очищается без разбора, и данные теряются. Оба исхода означают, что часть операций не выполнена, но никто этого не видит.
Практика: настройка и использование DLQ
1. Различайте временные и постоянные ошибки. Временные повторяются с задержкой, постоянные попадают в DLQ сразу.
2. Задавайте предел повторов. Бесконечный повтор расходует ресурсы и скрывает проблему.
3. Сохраняйте причину попадания. Причина, число попыток и время последней ошибки нужны для разбора.
4. Не обрабатывайте DLQ автоматически. Автоматический возврат сообщения в основную очередь воспроизводит исходную ошибку.
5. Мониторьте глубину DLQ. Накопление сообщений указывает на системную проблему, а не на единичный случай; о практике — в статье про обработку ошибок.
6. Установите срок хранения в DLQ. Сообщения хранятся достаточно долго для разбора, но не накапливаются годами.
7. Обеспечьте безопасный повтор. Возврат сообщения в обработку после исправления причины должен быть явной операцией с проверкой.
8. Разделяйте DLQ по типам ошибок. Отравленные сообщения и исчерпанные повторы требуют разного разбора.
Рекомендации:
- Не игнорируйте сообщения в DLQ. Очередь с накопленными сообщениями означает, что часть операций не выполнена.
- Проверяйте идемпотентность при повторе. Сообщение могло быть обработано частично перед ошибкой.
- Алертите на первое сообщение, а не на порог. Единичное отравленное сообщение — сигнал о неверных данных на входе.
- Документируйте процедуру разбора. Разбор DLQ выполняется в стрессовой ситуации, и описание шагов экономит время.
- Проверяйте, что DLQ настроена. Отсутствие настроенной очереди приводит к потере сообщений при первой же ошибке.
Практический сценарий: заблокированная очередь отравленным сообщением
Контекст: обработка заказов остановилась. Очередь росла, а обработчик в журнале повторял одну и ту же ошибку разбора.
- Диагностика. Одно сообщение содержало некорректную кодировку в поле наименования товара. Обработчик падал при разборе, сообщение возвращалось в очередь, и цикл повторялся. Остальные сообщения не обрабатывались, поскольку одно блокировало голову очереди.
- Причина. DLQ не была настроена, а предел повторов отсутствовал. Любая ошибка приводила к бесконечному повтору, и постоянная ошибка не отличалась от временной.
- Действия. (а) Настроили очередь недоставленных сообщений с маршрутизацией при исчерпании повторов; (б) установили предел в пять повторов с нарастающей задержкой; (в) разделили ошибки на временные и постоянные, чтобы неверный формат попадал в DLQ без повторов; (г) добавили метрику глубины DLQ с тревогой на первое сообщение; (д) разобрали накопившиеся сообщения и добавили проверку кодировки на входе.
- Результат. Некорректное сообщение больше не блокирует очередь, а попадание в DLQ обнаруживается немедленно вместо обнаружения по остановке обработки.
Вывод: отсутствие DLQ превращает постоянную ошибку в остановку всей очереди. Очередь недоставленных сообщений изолирует необработанные сообщения, но требует различения временных и постоянных ошибок, иначе повторы расходуют ресурсы без результата.
FAQ
Что делать с сообщениями в DLQ? Разбирать вручную: определить причину, исправить данные или код, при необходимости вернуть в обработку. Автоматический возврат воспроизводит ошибку.
Сколько повторов задать? Обычно три-пять с нарастающей задержкой. Большее число оправдано только для длительных внешних сбоев.
Нужна ли DLQ, если обработчик логирует ошибки? Да. Журнал фиксирует ошибку, но сообщение при этом теряется либо блокирует очередь. DLQ сохраняет его для разбора.
Как отличить отравленное сообщение? По повторяющейся ошибке разбора при нескольких попытках. Такие сообщения попадают в DLQ без исчерпания предела повторов.
Источники: RabbitMQ docs (Dead Letter Exchanges), AWS SQS docs (DLQ), Kafka docs — ссылки в шапке.