Отправка и получение сообщений через RabbitMQ в Symfony
Привет, Хабр!
Итак, решили внедрить асинхронные процессы в своё Symfony-приложение? Отличный выбор! А выбор RabbitMQ для этой задачи — вообще идеален: надёжный, быстрый и отлично работающий в связке с Symfony. Наша цель — научиться отправлять сообщения (скажем, сообщения о новых котиках) в очередь и плавно обрабатывать их.
Запуск RabbitMQ
Для начала понадобится сам RabbitMQ. Можно установить его напрямую или, как в большинстве проектов, с помощью Docker.
Добавляем в docker-compose.yml
сервис для RabbitMQ:
version: '3.8'
services:
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672" # для общения с приложением
- "15672:15672" # для панели управления
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
Теперь запускаем:
docker-compose up -d
Проверяем: панель управления RabbitMQ доступна по адресу http://localhost:15672
(логин и пароль — guest). Здесь можно управлять очередями, обменниками и проверять, как сообщения путешествуют по RabbitMQ.
Настройка Symfony Messenger
Теперь — к нашей основной задаче. Symfony Messenger — это мощный компонент, который облегчает отправку и получение сообщений. В нашем случае, это — сообщения о котиках. С помощью Messenger будем слать котиков в очередь и обрабатывать их с другой стороны.
Чтобы начать, нужно установить пару зависимостей:
composer require symfony/messenger symfony/amqp-messenger
В файле config/packages/messenger.yaml
добавляем транспорт для RabbitMQ. Это будет «транспорт асинхронных сообщений»:
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%' # Основной транспорт
routing:
'App\Message\CatMessage': async # Назначаем маршрут для наших сообщений
Здесь мы указали, что все сообщения типа CatMessage
будут отправляться в очередь «async».
Подключение к RabbitMQ
Добавляем следующую строку в файл .env
:
MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f
Тут мы указываем параметры подключения к RabbitMQ — дефолтные логин и пароль, адрес хоста и название виртуального хоста (%2f
— это код символа /
, он обозначает корневой виртуальный хост).
Создание сообщения CatMessage
Теперь создадим сообщение. В Symfony это — объект, который будет содержать данные о котике. Начнем с простого примера:
// src/Message/CatMessage.php
namespace App\Message;
class CatMessage
{
private string $name;
private int $age;
public function __construct(string $name, int $age)
{
$this->name = $name;
$this->age = $age;
}
public function getName(): string
{
return $this->name;
}
public function getAge(): int
{
return $this->age;
}
}
Теперь есть простой объект сообщения с именем и возрастом котика. Это то, что мы будем отправлять в RabbitMQ.
Обработчик сообщения
Сообщения у нас есть, но их нужно кому-то обрабатывать. Создадим обработчик, который будет получать сообщения из очереди и обрабатывать их.
// src/MessageHandler/CatMessageHandler.php
namespace App\MessageHandler;
use App\Message\CatMessage;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
class CatMessageHandler implements MessageHandlerInterface
{
public function __invoke(CatMessage $message)
{
// Представим, что обработчик добавляет котика в базу данных
echo sprintf("Котик %s, возраст %d лет, успешно обработан!\n", $message->getName(), $message->getAge());
}
}
Наш обработчик будет просто выводить информацию о котике, но в продакшене можно здесь сделать что-то полезное, например, добавить котика в базу данных.
Отправка сообщений
Пора отправить первого котика в очередь! Для создадим контроллер, который будет принимать запрос и отправлять CatMessage
через Symfony Messenger.
// src/Controller/CatController.php
namespace App\Controller;
use App\Message\CatMessage;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Annotation\Route;
class CatController
{
private MessageBusInterface $bus;
public function __construct(MessageBusInterface $bus)
{
$this->bus = $bus;
}
#[Route('/send-cat', name: 'send_cat')]
public function sendCat(): Response
{
$catMessage = new CatMessage('Барсик', 3);
$this->bus->dispatch($catMessage);
return new Response('Сообщение о котике отправлено!');
}
}
Теперь можно перейти по адресу /send-cat
, и сообщение отправится в очередь RabbitMQ. Каждый раз, когда этот маршрут вызывается, новый котик добавляется в очередь.
Запуск консумера (обработчика)
Чтобы Symfony мог начать забирать сообщения из RabbitMQ и передавать их в обработчик, нужно запустить консумер:
php bin/console messenger:consume async
Теперь при каждом новом сообщении обработчик будет выводить информацию о котике.
Обработка ошибок и ретраи
Жизнь сурова: иногда обработка сообщений может завершиться с ошибкой. Symfony Messenger позволяет настраивать ретраи и логировать ошибки.
Для этого в messenger.yaml
добавим параметры:
framework:
messenger:
failure_transport: failed
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
failed: 'doctrine://default?queue_name=failed'
retry_strategy:
async:
max_retries: 3
delay: 1000 # Задержка между ретраями
multiplier: 2 # Увеличиваем задержку на 2 раза
max_delay: 30000 # Максимальная задержка между ретраями
Здесь настроили стратегию ретраев, которая повторит отправку сообщения до трех раз с увеличением задержки. Если сообщение так и не будет обработано, оно переместится в failed
-транспорт, где мы сможем его повторно отправить позже.
Инлайн-классы
Если хочется экспериментировать с инлайн-классами (например, в тестовых сценариях), вы можно создать обработчик с помощью анонимных классов:
$handler = new class implements MessageHandlerInterface {
public function __invoke(CatMessage $message)
{
echo sprintf("Инлайн-обработчик: Котик %s, возраст %d лет, был обработан.\n", $message->getName(), $message->getAge());
}
};
Инлайн-классы полезны для быстрого прототипирования, но не стоит злоупотреблять!
Если у вас есть чем дополнить или поделиться своим опытом работы с RabbitMQ в Symfony — пишите в комментариях! Всегда интересно узнать.
Также спешу напомнить, что сегодня, 12 ноября, в 20:00 пройдет открытый урок на тему «Надёжная отправка и получение сообщений через RabbitMQ в Symfony». Если интересно, узнать подробности и записаться можно на странице курса «Symfony Framework».