Очереди в проекте: что ломается при внедрении RabbitMQ
Очередь не делает обработку надёжной сама по себе. При внедрении RabbitMQ нужно заранее решить, где подтверждать сообщение, как переживать повторы и что делать с задачами, которые постоянно падают.

Очередь в проекте обычно появляется после одного из двух событий: HTTP-запрос начинает ждать внешнюю систему дольше допустимого или фоновые задачи теряются при перезапуске приложения. RabbitMQ снимает часть нагрузки с веб-процесса, но добавляет новые точки отказа: повторную доставку, зависшие сообщения, переполненный брокер и рассинхрон между базой данных и очередью. Ниже — практический разбор внедрения для RabbitMQ 3.13 и 4.x без выдуманных бенчмарков: что выбрать, где поставить подтверждение и какие метрики снять до выхода в прод.
Начинайте не с брокера, а с границы задачи
Очередь нужна не для того, чтобы «ускорить всё приложение». Она отделяет короткую операцию от работы, которую можно выполнить позже. Хороший кандидат — отправка письма, пересчёт отчёта, обработка изображения, обращение к медленному API или синхронизация с внешней системой.
Плохой кандидат — операция, результат которой нужен пользователю в том же HTTP-ответе. Если клиент должен немедленно узнать, что платёж принят, переносить саму фиксацию платежа в фонового потребителя опасно. В очередь можно отправить уведомление или последующую сверку, но не скрывать от пользователя критичный результат.
- Вход: событие или команда с устойчивым идентификатором.
- Обработчик: одна конкретная операция, которую можно повторить.
- Результат: запись в базе, вызов внешней системы или новое событие.
- Ошибка: понятное решение — повторить, отложить или отправить в отдельную очередь.
Главный результат внедрения очереди — не сам RabbitMQ, а явный контракт: что считается принятым, что можно повторить и кто отвечает за окончательный результат.
Сноска. Идемпотентность — свойство операции, при котором повторное выполнение с теми же данными не создаёт второй побочный эффект. Например, повторная обработка события не должна дважды списывать деньги или создавать два одинаковых заказа.
Какую схему внедрять
В RabbitMQ продюсер обычно публикует сообщение в exchange, а exchange направляет его в одну или несколько очередей по правилам маршрутизации. Потребитель читает сообщение и подтверждает обработку. Такая цепочка полезнее прямой отправки в конкретную очередь: маршрутизацию можно менять без переписывания всех отправителей.
Команда и событие — разные сообщения
Команда просит выполнить действие: «пересчитать отчёт» или «отправить письмо». У неё обычно один логический обработчик. Событие сообщает, что действие уже произошло: «заказ оплачен». У него может быть несколько подписчиков — например, склад, уведомления и аналитика.
Не смешивайте эти модели в одной очереди без причины. Команда с обязательным исполнителем и событие с несколькими независимыми потребителями имеют разный жизненный цикл, разные политики повторов и разные требования к задержке.
Что положить в сообщение
В сообщение лучше передавать идентификатор сущности, тип события, версию схемы, время создания и уникальный идентификатор сообщения. Полную копию большой записи из базы класть не стоит: данные устареют, сообщение станет тяжёлым, а изменение схемы превратится в миграцию очереди.
Потребитель должен уметь получить актуальное состояние по идентификатору. Но это не отменяет проверки версии: событие о старом состоянии может прийти позже нового.
Подтверждение доставки не равно успешной обработке
У RabbitMQ есть несколько разных границ подтверждения, и именно здесь чаще всего появляется ложное ощущение надёжности.
- Продюсер публикует сообщение. Сетевой ответ ещё не означает, что брокер надёжно принял его. Для критичных сообщений нужны publisher confirms — подтверждения от RabbitMQ, что публикация принята.
- Потребитель получает сообщение. При ручном подтверждении сообщение остаётся незавершённым, пока обработчик не отправит acknowledgment.
- Обработчик меняет базу. Подтверждать сообщение до фиксации транзакции нельзя: при падении процесса работа будет считаться выполненной, хотя запись не сохранилась.
- Обработчик подтверждает сообщение. Если процесс упал между записью в базе и подтверждением, RabbitMQ может доставить сообщение повторно. Это нормальное поведение, а не баг.
Именно поэтому типичная гарантия для прикладной системы — at-least-once, то есть «не менее одного раза». Она защищает от потери, но допускает дубликаты. Гарантия «ровно один раз» не появляется от включения durable-очереди и persistent-сообщений.
Три подхода и их цена
| Подход | Когда подходит | Что выигрываете | За что платите |
|---|---|---|---|
| Синхронный HTTP-вызов | Ответ нужен в рамках одного запроса | Простая трассировка и понятный результат | Зависимость от доступности сервиса и времени ответа |
| RabbitMQ-команда | Работу можно выполнить позже, нужен контроль повторов | Буферизация, подтверждения, маршрутизация | Дубликаты, мониторинг, отдельная эксплуатация брокера |
| Событие через RabbitMQ | Одно действие должны обработать несколько подписчиков | Слабая связанность отправителя и получателей | Сложнее отлаживать порядок и повторную обработку |
| Потоковая платформа | Нужны длительное хранение и повторное чтение потока | Повторная обработка и работа с историей событий | Более тяжёлая модель эксплуатации и проектирования |
Если задача — обработать запрос один раз после ответа пользователю, RabbitMQ обычно проще потоковой платформы. Если нужно через месяц заново прочитать все события за период, одной очереди может оказаться недостаточно: срок хранения и модель повторного чтения нужно закладывать отдельно.
Что внедрить вместе с очередью
Outbox для связи с базой
Самый опасный сценарий выглядит так: приложение записало заказ в базу, но упало до публикации события. В базе заказ есть, а подписчики о нём не узнали. Обратная ситуация тоже возможна — событие ушло, а транзакция базы откатилась.
Паттерн outbox решает эту границу: приложение в одной транзакции сохраняет бизнес-изменение и запись о будущем сообщении в таблицу outbox. Отдельный отправитель читает outbox и публикует сообщения в RabbitMQ. После подтверждения публикации запись помечается обработанной. Это не отменяет идемпотентность: отправитель тоже может упасть после публикации и до отметки в таблице.
Повторы и очередь ошибок
Повторять нужно только временные ошибки: краткий сетевой сбой, временную недоступность внешнего сервиса, блокировку ресурса. Неверный формат сообщения, отсутствие обязательной сущности и нарушение бизнес-правила повтором не исправятся.
- ограничьте число попыток;
- увеличивайте интервал между попытками;
- разделяйте временные и постоянные ошибки;
- после исчерпания попыток отправляйте сообщение в dead-letter queue;
- храните причину последней ошибки и идентификатор сообщения.
Dead-letter queue — очередь для сообщений, которые нельзя обработать обычным потребителем. Это не мусорный контейнер: к ней нужен владелец, срок хранения и процедура повторного запуска после исправления причины.
Prefetch и размер обработки
Большой prefetch позволяет потребителю заранее получить много сообщений, но увеличивает число сообщений «в полёте». При падении процесса они будут доставлены заново, а другие потребители могут временно остаться без работы. Маленький prefetch снижает накопление, но может уменьшить пропускную способность.
Подбирайте значение по времени обработки и памяти сообщения, а не копируйте число из чужого проекта. Снимите время обработки одного задания, число параллельных потребителей и размер очереди. После изменения сравнивайте одинаковые интервалы и одинаковый объём входящих данных.
Что ломается первым
Двойная обработка
Пользователь дважды нажал кнопку, отправитель повторил публикацию, потребитель упал после записи в базу — источников дубликатов несколько. Защитой служит таблица обработанных идентификаторов, уникальный ключ на бизнес-операцию или атомарная операция в самой базе. Проверка «сначала прочитать, потом вставить» без ограничения уникальности уязвима при параллельной обработке.
Порядок сообщений
Порядок доставки нельзя считать глобальной гарантией, если работают несколько потребителей, есть повторы или сообщение повторно отправляется после ошибки. Если порядок важен для одной сущности, проектируйте ключ сериализации для этой сущности и отдельно проверяйте, что более старое событие не перезаписывает новое.
Зависшие сообщения
Метрика «очередь почти пустая» не означает, что система здорова. Сообщения могут находиться в состоянии unacked: потребитель их получил, но не подтвердил. Смотрите одновременно на ready, unacked, возраст самого старого сообщения, скорость публикации и скорость успешного подтверждения.
Огромная очередь
Очередь — буфер, а не бесконечное хранилище. Если скорость поступления выше скорости обработки, задержка будет расти даже при исправных серверах. Заранее определите порог, после которого вы снижаете входной поток, добавляете потребителей или отключаете необязательную функцию.
Где подход ломается
RabbitMQ не исправит обработчик, который выполняется часами и удерживает сообщение без понятного тайм-аута. Для длинных задач нужны контроль прогресса, возможность безопасного продолжения и политика зависших сообщений. Не отправляйте в одну очередь задания с разным порядком важности: срочные операции могут оказаться за большим массивом дешёвых фоновых задач.
Отдельный риск — изменение схемы сообщения. Потребители обновляются не одновременно, поэтому новая версия должна некоторое время сосуществовать со старой. Добавляйте поля обратно совместимо, не переименовывайте обязательные поля без миграции и храните версию контракта.
Замеры «до и после» нельзя честно назвать без условий. Для каждого типа задачи фиксируйте размер сообщения, число потребителей, параллельность, версию RabbitMQ, характеристики узлов, скорость публикации и процент ошибок. Минимальный набор результатов — задержка от публикации до начала обработки, время обработки, доля повторов, возраст старейшего сообщения и число сообщений в dead-letter queue.
Что делать дальше
Начните с одной некритичной, но измеримой задачи. Опишите контракт сообщения, добавьте уникальный идентификатор и ручное подтверждение, затем смоделируйте падение потребителя после записи в базу. После этого проверьте повторную доставку, переполнение очереди и восстановление из dead-letter queue.
В проде отдельно наблюдайте publisher confirms, ready и unacked-сообщения, возраст очереди, ошибки потребителей, дисковое пространство брокера и долю повторов. Следующим шагом обычно становится не увеличение числа воркеров, а устранение причины: слишком медленный внешний API, отсутствующая идемпотентность или несогласованная транзакция outbox.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: RabbitMQ Documentation: Queues, RabbitMQ Documentation: Consumer Acknowledgements and Publisher Confirms
Частые вопросы
Можно ли считать сообщение обработанным сразу после получения потребителем?
Почему одно сообщение RabbitMQ пришло два раза?
Нужен ли RabbitMQ, если задач немного?
Что делать, если очередь быстро растёт?
Подойдёт ли эта схема для другой версии RabbitMQ?
Что проверить, если потребитель подключён, но сообщения не обрабатываются?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.