Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Очереди сообщений

Создание отчёта, обработка картинки, рассылка занимают секунды и минуты. Заставлять посетителя ждать нельзя. Компонент Messenger откладывает такую работу: контроллер кладёт сообщение в очередь и сразу отвечает, а отдельный процесс выполняет задачу в фоне.

Сообщение

Сообщение — класс с данными, нужными для работы:

// src/Message/GenerateReport.php
namespace App\Message;
 
class GenerateReport
{
    public function __construct(
        public readonly int $userId,
        public readonly string $period,
    ) {}
}

Обработчик

// src/MessageHandler/GenerateReportHandler.php
namespace App\MessageHandler;
 
use App\Message\GenerateReport;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
 
#[AsMessageHandler]
class GenerateReportHandler
{
    public function __construct(private UserRepository $users) {}
 
    public function __invoke(GenerateReport $message): void
    {
        $user = $this->users->find($message->userId);
 
        // долгая работа: собрать отчёт и отправить его
    }
}

Какое сообщение обрабатывать, Symfony определяет по типу аргумента.

Отправка

use Symfony\Component\Messenger\MessageBusInterface;
 
#[Route('/report', name: 'report', methods: ['POST'])]
public function report(MessageBusInterface $bus): Response
{
    $bus->dispatch(new GenerateReport($this->getUser()->getId(), '2026-09'));
 
    $this->addFlash('success', 'Отчёт готовится и придёт на почту');
 
    return $this->redirectToRoute('profile');
}

Без дополнительной настройки обработчик выполнится сразу, в том же запросе.

Асинхронный транспорт

Транспорт — место, где хранится очередь. Проще всего начать с базы данных:

MESSENGER_TRANSPORT_DSN=doctrine://default
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async: '%env(MESSENGER_TRANSPORT_DSN)%'
        routing:
            App\Message\GenerateReport: async

Раздел routing указывает, какие сообщения отправлять в очередь. Для большой нагрузки вместо базы используют Redis или RabbitMQ — меняется только строка в .env.

Запуск обработчика

php bin/console messenger:consume async -vv

Команда работает постоянно и выполняет сообщения по мере появления. На сервере её запускают под управлением Supervisor или systemd, чтобы процесс перезапускался после сбоя.

Задержка

use Symfony\Component\Messenger\Stamp\DelayStamp;
 
$bus->dispatch(new SendReminder($orderId), [new DelayStamp(60 * 60 * 1000)]);

Время задаётся в миллисекундах: это сообщение выполнится через час.

Неудачные сообщения

Если обработчик бросил исключение, Messenger повторит попытку — по умолчанию три раза с растущей паузой. После этого сообщение попадает в отдельную очередь:

php bin/console messenger:failed:show
php bin/console messenger:failed:retry

Частая ошибка

Обработчик — долго живущий процесс: он загрузил код один раз и новых файлов не видит. После обновления сайта он продолжит выполнять старую версию. При каждом размещении выполняйте php bin/console messenger:stop-workers — процессы завершатся после текущего сообщения, и Supervisor запустит их заново. И передавайте в сообщении идентификатор, а не сущность: к моменту выполнения данные в базе могут измениться.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.