RabbitMQ на практике: обменники, routing, DLQ и мониторинг
В предыдущих статьях курса мы настроили Django + PostgreSQL и подключили Celery к RabbitMQ как к брокеру сообщений. На этом этапе система уже работает, но в реальном проекте быстро возникают вопросы:
Почему часть задач должна идти в отдельные очереди и как это правильно настроить?
Как понять, куда именно попало сообщение и почему оно не обрабатывается?
Что делать с “ядовитыми” сообщениями, которые постоянно падают?
Как мониторить RabbitMQ, чтобы не узнавать о проблемах от пользователей?Эта статья даёт практическое понимание RabbitMQ: обменники, маршрутизация, dead-letter (DLQ) и базовый мониторинг.
Официальные источники:
RabbitMQ Documentation
RabbitMQ Management Plugin
Celery Documentation
Kombu Documentation!Общая схема маршрутизации сообщений и попадания в DLQ
Минимальная анатомия RabbitMQ
Чтобы уверенно настраивать Celery и диагностировать проблемы, нужно различать базовые сущности.
Сообщение — данные, которые нужно доставить потребителю (в нашем случае это описание Celery-задачи и её аргументы).
Producer — тот, кто публикует сообщение (обычно Django-код, который вызывает .delay() или .apply_async()).
Consumer — тот, кто читает сообщения (Celery worker).
Очередь (queue) — буфер, где сообщения ждут обработки.
Обменник (exchange) — точка входа публикации: producer отправляет сообщение в exchange, а уже exchange решает, в какие очереди оно попадёт.
Привязка (binding) — правило, которое связывает exchange и очередь.
Routing key — строка в сообщении, по которой exchange может маршрутизировать сообщение.Ключевая мысль: сообщение публикуют не “в очередь”, а “в обменник”, а дальше вступают правила маршрутизации.
Типы обменников и как выбрать
RabbitMQ поддерживает несколько типов exchange. На практике чаще всего вам нужен direct или topic.
| Тип exchange | Как маршрутизирует | Когда использовать |
|---|---|---|
| direct | Точное совпадение routing key | Простое разнесение задач по очередям: emails, reports, default |
| topic | Шаблоны по routing key (например, emails.*) | Когда нужна иерархия и гибкость, много видов задач |
| fanout | Всем привязанным очередям | Broadcast-события, когда все должны получить копию |
| headers | По заголовкам сообщения | Редко, специфичные случаи |
Если вы только начинаете: используйте direct и отдельные очереди под классы задач. Если в проекте появляется сложная матрица маршрутов: переходите на topic.
Как Celery использует RabbitMQ по умолчанию
В предыдущей статье мы настроили Celery и начали отправлять задачи через RabbitMQ. Важно понимать, что Celery строит топологию RabbitMQ (exchange/queue/binding) автоматически.
Типичный минимум:
есть очередь по умолчанию, например default
задачи без специальных правил маршрутизации попадают тудаВ Django-коде вы делаете:
Дальше Celery:
сериализует задачу в сообщение
публикует сообщение в брокер
воркер читает сообщение из очереди и выполняет задачуНо как только вам нужно:
отделить emails от тяжёлых reports
иметь отдельный воркер на “критичные” задачи
изолировать нестабильные интеграциивам нужна явная маршрутизация.
Routing на практике: очереди, ключи и воркеры
Бизнес-идея очередей
Чаще всего очереди в проекте делят по профилю нагрузки:
default — обычные задачи
emails — всё, что связано с отправкой писем
cpu — тяжёлые CPU-задачи
integrations — нестабильные внешние APIТогда вы можете запускать разные воркеры и управлять ресурсами.
Маршрутизация в Celery через CELERY_TASK_ROUTES
В предыдущей статье мы уже использовали CELERY_TASK_ROUTES. Это самый простой способ:
После этого запускайте воркеры так, чтобы они слушали “свои” очереди:
Когда этого недостаточно
CELERY_TASK_ROUTES отвечает на вопрос “в какую очередь класть задачу”. Но RabbitMQ умеет больше:
routing key
разные exchange
dead-letter exchange
TTLДля этого Celery использует Kombu-структуры Exchange и Queue.
Явная топология: exchange, routing key и объявления очередей
Ниже пример практичной схемы:
один topic-exchange tasks
routing key вида emails.send, reports.generate
очереди подписываются на шаблоныЧто это даёт:
вы видите маршруты как часть архитектуры
можно легко расширять группы задач
проще анализировать сообщения в RabbitMQ (по ключам)DLQ: что это и зачем нужно
DLQ (dead-letter queue) — “кладбище” сообщений, которые не были успешно обработаны.
Важно: в RabbitMQ чаще говорят не “DLQ как функция”, а используют связку:
DLX (dead-letter exchange) — обменник, куда RabbitMQ перенаправляет “проблемные” сообщения
DLQ — очередь, привязанная к DLX, где эти сообщения накапливаютсяКогда сообщение попадает в DLQ
RabbitMQ может отправить сообщение в DLX в нескольких распространённых случаях:
Consumer отклонил сообщение
У сообщения истёк TTL
Переполнение очереди (например, из-за ограничений длины)В контексте Celery самый частый сценарий — отклонение (reject) или “неуспешная” обработка, когда вы явно говорите брокеру: это сообщение обработать не смогли, не возвращай в эту же очередь бесконечно.
Почему DLQ полезна именно в Celery
У вас есть два принципиально разных класса падений:
временные ошибки: сеть, таймауты, кратковременная недоступность API
постоянные ошибки: некорректные данные, логическая ошибка, несовместимый форматВременные ошибки логично ретраить.
Постоянные ошибки нужно:
остановить, чтобы не забивать очередь
зафиксировать для расследованияDLQ — один из механизмов, который помогает “убрать в сторону” проблемные сообщения.
DLQ в RabbitMQ через аргументы очереди
Чтобы включить dead-letter, очередь объявляют со специальными аргументами:
x-dead-letter-exchange — имя DLX
x-dead-letter-routing-key — routing key, с которым сообщение попадёт в DLX (не всегда обязательно)Пример: все сообщения из default при dead-letter будут уходить в dlx, а затем в очередь dead.
Что важно понимать:
DLQ не “лечит” ошибки автоматически
DLQ делает ошибки управляемыми: вы можете смотреть, сколько их, какие они, и принимать решенияTTL и “отложенная повторная попытка” через DLX
TTL (time-to-live) — время жизни сообщения. Если TTL истёк до обработки, RabbitMQ считает сообщение “просроченным”. Часто такие сообщения отправляют в DLX.
Практический паттерн:
задача не должна выполняться прямо сейчас
вы хотите отложить её повтор на секунд
вы не хотите, чтобы воркер держал сообщение “у себя” и ждалТогда делают “retry-очередь”:
у неё стоит TTL
по истечении TTL сообщение dead-letter’ится обратно в рабочую очередьПример концепции на уровне очередей:
retry_30s имеет TTL 30 секунд
retry_30s настроена на DLX обратно в tasks / defaultПояснения:
x-message-ttl задаётся в миллисекундах
сообщение, попав в retry_30s, “полежит” 30 секунд
после чего уйдёт в defaultВажно: этот паттерн полезен, но требует дисциплины в коде. Если вы автоматически будете перекидывать любую ошибку в retry-очередь, можно получить бесконечный цикл. Поэтому всегда ограничивайте число попыток и фиксируйте итоговый статус в PostgreSQL (мы к этому вернёмся дальше по курсу).
Что смотреть в RabbitMQ Management
У вас уже подключён образ rabbitmq:3-management, а значит доступен веб-интерфейс. Он нужен не “для красоты”, а для ответов на диагностические вопросы.
Откройте интерфейс по адресу http://127.0.0.1:15672/ и проверьте:
Queues
Exchanges
Connections и ChannelsОчереди: базовые метрики, которые решают 80% проблем
В списке очередей обычно важнее всего:
Ready — сколько сообщений ждут обработки
Unacked — сколько сообщений выдано consumer’ам, но ещё не подтверждено
Consumers — сколько потребителей реально читает очередь
скорость поступления и обработки сообщений (rates)Типовые интерпретации:
Ready растёт, Consumers = 0 — воркеры не запущены или не слушают эту очередь
Unacked растёт и не падает — воркеры “подвисли”, задачи слишком долгие, либо проблемы с подтверждениями
Ready растёт быстрее, чем обработка — не хватает воркеров или задача стала существенно тяжелееExchanges и bindings: куда “подписана” очередь
Если сообщение “не приходит”, часто причина в маршрутизации:
exchange не тот
routing key не совпадает
binding отсутствуетВ UI можно открыть exchange и увидеть, какие есть bindings (куда он отправляет сообщения).
Базовая диагностика через rabbitmqctl
Если вы работаете в окружении, где UI недоступен, полезно знать CLI-команды.
Если RabbitMQ запущен в Docker, команды выполняются внутри контейнера:
Другие практичные команды:
Практические рекомендации для связки Django + Celery + RabbitMQ
Разносите очереди по профилю задач: это дешевле и надёжнее, чем пытаться “одним воркером” обслужить всё.
Следите за Unacked: при acks_late это индикатор зависших или слишком долгих задач.
Делайте задачи идемпотентными: при сбоях доставки и падениях воркеров возможны повторы.
Используйте DLQ для управляемого фейла: особенно для постоянных ошибок и “ядовитых” сообщений.
Фиксируйте состояние выполнения в PostgreSQL: RabbitMQ отвечает за доставку, но не за бизнес-состояние.Итог
RabbitMQ в связке с Celery — это не просто “брокер, который работает”. Это управляемая система маршрутизации и надёжной доставки.
В этой статье вы разобрали:
чем отличаются queue и exchange, что такое binding и routing key
какие типы exchange бывают и что чаще использовать в Celery-проектах
как настраивать маршрутизацию задач по очередям и ключам
как устроены DLX/DLQ и зачем они нужны
как применять TTL и DLX для отложенных повторов
какие метрики смотреть в RabbitMQ Management и как быстро диагностировать проблемыДальше по курсу логично перейти к “приземлению” этого в бизнес-процесс: хранение статусов задач в PostgreSQL, обработка ошибок, ограничения ретраев и предсказуемое восстановление после сбоев.