Содержание
Очередь задач нужна, чтобы всё медленное и ненадёжное — отправка писем, генерация отчётов, обработка файлов, вызовы внешних API — выполнялось в фоне, а пользователь получал ответ сразу. Веб-запрос ставит задачу в очередь за миллисекунды, а отдельные процессы-обработчики выполняют её, повторяют при сбое и не дают одной медленной операции положить весь сайт.
Разберём, как устроены очереди, что в них выносить и как не потерять задачи.
Зачем веб-приложению очередь задач?
Очередь задач нужна веб-приложению, чтобы отделить быстрый ответ пользователю от долгой или ненадёжной работы. Пока всё выполняется внутри запроса, любая задержка внешнего сервиса превращается в задержку для пользователя, а любой сбой — в ошибку на экране.
Что обычно выносят в очередь:
- отправку писем, SMS и push-уведомлений;
- обработку загруженных файлов — изображений, видео, документов;
- генерацию отчётов и выгрузок;
- вызовы внешних API: платёжных систем, CRM, служб доставки;
- обращения к AI-моделям, которые отвечают секундами;
- пересчёт агрегатов и индексов для поиска.
Правило простое: если операция не нужна для ответа пользователю прямо сейчас, её место в очереди.
Как устроена очередь задач?
Очередь задач состоит из трёх частей: приложение ставит задачу, брокер хранит её, а процессы-обработчики (воркеры) забирают и выполняют. Между постановкой и выполнением задача живёт в хранилище и переживает перезапуск приложения.
| Часть | Что делает | Типичные варианты |
|---|---|---|
| Продюсер | Кладёт задачу с данными в очередь | Код приложения: контроллер, событие, планировщик |
| Брокер | Хранит задачи до выполнения | Redis, база данных, RabbitMQ, облачные очереди |
| Воркер | Забирает задачу и выполняет её | Отдельные процессы, масштабируемые по нагрузке |
| Хранилище неудачных задач | Сохраняет задачи, которые не удалось выполнить | Таблица failed jobs, отдельная очередь |
| Мониторинг | Показывает длину очередей и ошибки | Панели фреймворка, метрики и алерты |
В большинстве фреймворков очереди есть из коробки: Laravel Queues, BullMQ для Node.js, Celery для Python. Для старта обычно хватает Redis или даже самой базы данных в роли брокера.
Фото: Unsplash
Как написать надёжную фоновую задачу?
Надёжная фоновая задача должна быть идемпотентной, ограниченной по времени и повторяемой: её можно безопасно выполнить дважды, она не зависает навсегда и при временном сбое повторяется с паузой. Большинство проблем с очередями возникает из-за нарушения одного из этих правил.
Пример задачи в Laravel, которая отправляет заказ в CRM:
final class SendOrderToCrm implements ShouldQueue
{
use Queueable;
public int $tries = 5;
public int $timeout = 30;
public function __construct(public int $orderId) {}
/** Пауза между попытками растёт: 10 с, 1 мин, 5 мин… */
public function backoff(): array
{
return [10, 60, 300, 900];
}
public function handle(CrmClient $crm): void
{
$order = Order::findOrFail($this->orderId);
// Идемпотентность: повторный запуск не создаст дубль в CRM
if ($order->crm_id !== null) {
return;
}
$order->update(['crm_id' => $crm->createDeal($order)]);
}
}
// В контроллере — мгновенно, пользователь не ждёт CRM
SendOrderToCrm::dispatch($order->id);
Правила, которые экономят нервы
- Передавайте в задачу идентификатор, а не весь объект: к моменту выполнения данные могут измениться.
- Ставьте задачу в очередь после коммита транзакции, иначе воркер может не найти ещё не сохранённую запись.
- Разделяйте очереди по приоритету: письма о сбросе пароля не должны ждать за тысячей отчётов.
- Ограничивайте время выполнения и число попыток — бесконечные повторы забивают очередь.
Как не потерять задачи и заметить сбой?
Задачи не теряются, если брокер хранит их надёжно, неудачные попытки сохраняются отдельно, а длина очереди и число ошибок отслеживаются с оповещениями. Самая опасная ситуация — тихая: воркеры упали, задачи копятся, а заметили это только по жалобам клиентов.
Что настроить:
- автоматический перезапуск воркеров через менеджер процессов или оркестратор;
- перезапуск воркеров при каждом деплое, чтобы они подхватили новый код;
- хранилище неудачных задач и регулярный разбор причин;
- алерт на длину очереди и на время ожидания задачи;
- алерт на рост числа ошибок в конкретном типе задач.
Когда нужен отдельный брокер сообщений?
Отдельный брокер вроде RabbitMQ или Kafka нужен, когда очередь становится средством обмена между несколькими сервисами или объём событий измеряется тысячами в секунду. Для фоновых задач внутри одного приложения чаще хватает Redis или базы данных.
Признаки, что пора смотреть на брокер:
- задачи ставит одно приложение, а выполняет другое, написанное на другом стеке;
- одно событие должны получить несколько независимых потребителей;
- нужна гарантированная доставка с подтверждениями и сложной маршрутизацией;
- нужно хранить поток событий и перечитывать его заново.
Это решение того же рода, что и выбор между монолитом и сервисами, — подробнее в статье монолит или микросервисы. Для вызовов AI-моделей очередь особенно полезна; об этом — в статье как встроить LLM в продукт.
Коротко
- В очередь выносят всё, что не нужно для ответа пользователю прямо сейчас.
- Очередь — это продюсер, брокер, воркеры, хранилище неудачных задач и мониторинг.
- Задача должна быть идемпотентной, ограниченной по времени и повторяемой с паузой.
- Ставьте задачу после коммита, разделяйте очереди по приоритету и следите за их длиной.
- Отдельный брокер нужен для обмена между сервисами и больших потоков событий.
Частые вопросы
Можно ли использовать базу данных вместо Redis для очередей?
Да, для небольшой нагрузки это нормальный вариант: не нужна дополнительная инфраструктура, а задачи хранятся надёжно. При росте числа задач Redis или специализированный брокер работают быстрее и меньше нагружают базу.
Сколько воркеров нужно запускать?
Столько, чтобы очередь не росла в пиковые часы. Начинают с нескольких процессов и масштабируют по длине очереди и времени ожидания задач; тяжёлым задачам лучше выделить отдельные воркеры.
Чем очередь отличается от планировщика задач (cron)?
Планировщик запускает задачи по расписанию, а очередь — по событию, когда задачу поставили. Часто они работают вместе: планировщик раз в час ставит в очередь задачи на пересчёт, а воркеры их выполняют.



