Разработка · 7 мин чтения

Laravel Queue очереди: как настроить и когда использовать

Laravel Queue очереди спасают в тот момент, когда запрос пользователя не должен ждать, пока отработает медленная операция - отправка письма, запрос к СДЭК, генерация PDF или вызов внешнего API оплаты. Я выношу в очередь любую задачу, которая не обязана выполниться прямо сейчас, и за несколько лет работы с Laravel это, пожалуй, один из немногих механизмов фреймворка, который использую в каждом втором проекте с бэкендом.

Что такое очереди Laravel Queue и зачем они нужны

Без очереди контроллер делает всё синхронно: принял запрос, обратился к внешнему сервису, дождался ответа, вернул результат пользователю. Если внешний сервис отвечает 3-5 секунд - а СДЭК и банковские эквайринги любят так отвечать под нагрузкой - пользователь всё это время смотрит на крутящийся спиннер, а PHP-воркер занят одним запросом вместо того, чтобы обрабатывать следующие.

Очередь разрывает эту цепочку. Контроллер создаёт задачу (Job), кладёт её в очередь и сразу отвечает пользователю. Отдельный процесс - воркер - забирает задачи из очереди и выполняет их в фоне, независимо от HTTP-запроса. На практике я выношу в очередь:

  • отправку писем и push-уведомлений после регистрации или заказа;
  • обработку вебхуков от платёжных систем вроде T‑Bank - сам вебхук нужно подтвердить за доли секунды, а логику начисления заказа можно обработать через пару секунд;
  • создание заказа и получение трек-номера в СДЭК после оплаты;
  • рассылку сообщений через Telegram-бота большому списку пользователей;
  • генерацию отчётов, PDF-счетов, экспортов в Excel;
  • синхронизацию данных с CRM или сторонним API.

Общее правило простое: если задача занимает больше 200-300 мс или зависит от внешнего сервиса, который может залагать или упасть, - это кандидат в очередь.

Драйверы очередей Laravel: database, Redis, SQS и что выбрать

Laravel из коробки поддерживает несколько драйверов, и выбор между ними - это выбор между простотой и производительностью.

Драйвер Нужна доп. инфраструктура Скорость Когда беру
sync нет задачи выполняются мгновенно, в том же запросе только для локальной разработки и тестов
database нет, использует MySQL/PostgreSQL средняя, до пары сотен задач в секунду небольшие и средние проекты, когда не хочется поднимать Redis ради очереди
redis да, нужен Redis-сервер высокая проекты с нагрузкой, где важна скорость обработки и нужны приоритеты очередей
Amazon SQS да, облачный сервис AWS высокая, с автомасштабированием инфраструктура уже в AWS, нужна отказоустойчивость без своего сервера очередей
beanstalkd да, отдельный демон высокая встречается редко, обычно в legacy-проектах

На старте почти всегда ставлю database - он не требует ничего, кроме миграции, и для 90% проектов с интернет-магазином или CRM его хватает с запасом. На Redis перехожу, когда в очереди больше пары тысяч задач в день или нужны отложенные задачи (delayed jobs) с точным временем выполнения - database-драйвер тоже это умеет, но Redis делает это заметно быстрее под нагрузкой.

Настройка очереди Laravel Queue с нуля

Для database-драйвера порядок действий такой:

php artisan queue:table
php artisan migrate

В .env указываю драйвер:

QUEUE_CONNECTION=database

Дальше создаю класс задачи:

php artisan make:job SendOrderToSdek

Внутри job передаю данные через конструктор и описываю логику в методе handle:

class SendOrderToSdek implements ShouldQueue
{
 use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

 public $tries = 3;
 public $backoff = 30;

 public function __construct(public Order $order) {}

 public function handle(SdekService $sdek): void
 {
 $sdek->createShipment($this->order);
 }
}

Запускаю задачу из контроллера или события одной строкой:

SendOrderToSdek::dispatch($order);

Важный момент, который упускают новички: без запущенного воркера задачи просто копятся в таблице jobs и не выполняются. Очередь - это не магия, а таблица (или список в Redis) плюс отдельный процесс, который её вычитывает.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Supervisor и запуск воркеров в продакшене

Локально воркер можно запустить командой:

php artisan queue:work --tries=3 --timeout=90

Но в проде этот процесс должен работать постоянно и перезапускаться при падении или после каждого деплоя - код воркера кешируется в памяти PHP, и без перезапуска он продолжит работать со старой версией классов. Для этого использую Supervisor:

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/site/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/site/storage/logs/worker.log
stopwaitsecs=3600

Параметр numprocs=2 запускает два параллельных воркера - это уже даёт параллельную обработку задач без лишней сложности. После каждого деплоя обязательно выполняю php artisan queue:restart - команда мягко останавливает воркеры после текущей задачи, а Supervisor их тут же поднимает заново с новым кодом. У меня в готовых конфигурациях для проектов на Laravel есть отработанный шаблон Supervisor-конфига для очередей, который экономит время на первой настройке сервера.

Важно не путать queue:work и queue:listen - listen перезагружает приложение на каждую задачу и удобен для разработки, но в проде это лишний расход ресурсов. work держит приложение в памяти и работает быстрее, но требует restart после деплоя.

Retry, failed jobs и мониторинг фоновых задач

Задачи падают - эквайринг вернёт 500‑ю ошибку, СДЭК уйдёт в даунтайм на техобслуживание, стороннее API отдаст таймаут. Laravel сохраняет упавшие задачи в таблицу failed_jobs, если она создана:

php artisan queue:failed-table
php artisan migrate

Посмотреть упавшие задачи и перезапустить их вручную:

php artisan queue:failed
php artisan queue:retry all

В самом Job классе задаю количество попыток и задержку между ними через $tries и $backoff - для интеграции с СДЭК обычно ставлю 3 попытки с нарастающей паузой в 30, 60 и 120 секунд, потому что мгновенный повтор при перегрузке стороннего API почти всегда упадёт с той же ошибкой. Метод failed() в классе задачи даёт возможность отправить уведомление в Telegram-бота на aiogram или в свой канал мониторинга, если задача не выполнилась после всех попыток:

public function failed(Throwable $exception): void
{
 Notification::route('telegram', config('services.telegram.chat_id'))
 ->notify(new JobFailedNotification($this->order, $exception));
}

Для проектов на Redis-драйвере ставлю Laravel Horizon - он даёт дашборд с метриками по очередям, throughput и временем выполнения задач в реальном времени, плюс удобное управление приоритетами очередей через конфиг вместо ручного распределения воркеров.

Когда очереди нужны, а когда это лишняя сложность

Очередь - это ещё один движущийся компонент системы: отдельный процесс, который может упасть, застрять или не подхватить деплой. Для лендинга с формой обратной связи или простого сайта на Tilda с кастомным скриптом очередь не нужна - там задача одна и выполняется за 100-200 мс синхронно.

Я ставлю очередь, когда выполняется хотя бы одно из условий:

  • операция зависит от внешнего API, который может отвечать медленно или падать;
  • задачу нужно повторить при ошибке без участия пользователя;
  • объём работы разовый, но большой - рассылка по 5000 подписчикам разом;
  • несколько независимых действий можно выполнить параллельно вместо последовательного ожидания.

Есть и альтернатива очередям в коде - вынести асинхронную логику в n8n, если сценарий скорее про интеграцию сервисов между собой (пришёл заказ - создать задачу в CRM - отправить письмо - уведомить в Telegram), а не про бизнес-логику приложения. Я обычно провожу границу так: если асинхронная задача тесно завязана на модели и логику самого приложения - это Job в очереди Laravel; если это склейка нескольких внешних сервисов без сложной логики - это кандидат на n8n-сценарий, там же проще редактировать порядок шагов без деплоя кода.

Для проектов, где очереди нужны с нуля - новый бэкенд на Laravel с интеграциями оплаты и доставки, я закладываю такую архитектуру сразу в разработку, а не добавляю задним числом: переписывать синхронные контроллеры под очереди на живом проекте с данными в проде выходит дороже и рискованнее, чем сделать правильно с первой итерации.

API и серверная часть под SPA

API / Бэкенд

от 100 000 ₽

Подробнее →

Частые вопросы

Чем очередь отличается от cron-задачи в Laravel?

Cron (Task Scheduling в Laravel) запускает задачу по расписанию - раз в час, раз в сутки, независимо от того, произошло ли какое-то событие. Очередь реагирует на конкретное действие в приложении и выполняется как можно быстрее после того, как задача в неё попала. Часто используются вместе: cron раз в 5 минут ставит в очередь пачку задач на отправку писем, а воркеры их разбирают.

Можно ли использовать несколько очередей одновременно?

Да, и я почти всегда так делаю на проектах с разной приоритетностью задач. Например, очередь high для отправки данных в платёжный шлюз и обработки вебхуков, default для писем и уведомлений, low для отчётов и экспортов. Воркер запускается с указанием порядка: php artisan queue:work --queue=high,default,low - так критичные задачи не застревают за длинными фоновыми отчётами.

Что будет, если воркер упадёт во время обработки задачи?

Зависит от драйвера и настроек. У database и Redis есть механизм timeout - если воркер не подтвердил выполнение задачи за отведённое время (по умолчанию 60 секунд), задача считается зависшей и возвращается в очередь для повторной обработки. Поэтому важно задавать идемпотентную логику в handle() - при повторном запуске задача не должна создавать дубль заказа или письма, я обычно добавляю проверку по уникальному идентификатору перед выполнением действия.

Нужен ли Redis для очередей или хватит database-драйвера?

Для старта и среднего объёма задач - до нескольких тысяч в день - database-драйвера достаточно, он не требует отдельного сервера и настраивается за 5 минут. Redis имеет смысл подключать, когда очередь становится узким местом: задачи копятся быстрее, чем воркеры успевают их разбирать, либо нужны отложенные задачи с точным временем и высокая скорость записи под пиковую нагрузку, например в дни распродаж на интернет-магазине.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Самозанятый Калинкин Н. А. · работаю с физлицами и юрлицами

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно