Компонент Messenger

Компонент Messenger помогает приложениям отправлять и принимать сообщения из других приложений или через очереди сообщений.

Компонент во многом был основан на серии постов Маттиаса Нобака об автобусах команд и проект SimpleBus.

Установка

1
$ composer require symfony/messenger

Как вариант, вы можете клонировать хранилище https://github.com/symfony/messenger.

Note

If you install this component outside of a Symfony application, you must require the vendor/autoload.php file in your code to enable the class autoloading mechanism provided by Composer. Read this article for more details.

Концепты

../_images/overview.png
Отправитель:
Отвечает за сериализацию и отправку сообщений чему-то. Это что-то может быть брокером сообщений или сторонней API, к примеру.
Получатель:
Отвечает за десериализацию и перенаправление сообщений обработчку(ам). Это может быть пулер очереди сообщений или конечная точка API, к примеру.
Обработчик:
Отвечает за обработку сообщений, используя бизнес-логику, применимую к сообщениям.

Автобус

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

При использовании автобуса сообщений с пакетом Symfony FrameworkBundle, для вас конфигурируется следующее связующее ПО:

  1. LoggingMiddleware (ведет логи обработки ваших сообщений)
  2. SendMessageMiddleware (включает асинхронную обработку)
  3. HandleMessageMiddleware (вызывает зарегистрированный идентификатор)

Пример:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
use App\Message\MyMessage;
use Symfony\Component\Messenger\MessageBus;
use Symfony\Component\Messenger\HandlerLocator;
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;

$bus = new MessageBus([
    new HandleMessageMiddleware(new HandlerLocator([
        MyMessage::class => $handler,
    ])),
]);

$result = $bus->dispatch(new MyMessage(/* ... */));

Note

Каждое связующее ПО должно реализовывать MiddlewareInterface.

Обработчики

После диспетчеризации в автобус, сообщения будут обработаны "обработчиками сообщений". Обработчик сообщений - это PHP вызываемое (т.е. функция или экземпляр класса), которое будет проводить необходимую обработку для вашего сообщения:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
namespace App\MessageHandler;

use App\Message\MyMessage;

class MyMessageHandler
{
   public function __invoke(MyMessage $message)
   {
       // Обработка сообщения...
   }
}

Транспорт

Для отправки и получения сообщений вам понадобится сконфигурировать транспорт. Транспорт будет отвечать за коммуникацию с вашим брокером сообщений или третьими сторонами.

Ваш собственный отправитель

Используя SenderInterface вы можете с лёгкостью создать ваш собственный отправитель сообщений. Представьте, что у вас уже есть сообщение ImportantAction, которое проходит через автобус сообщений и обрабатывается обработчиком. Теперь, вам также надо отправить это сообщение в виде электронного письма.

Для начала, создайте ваш отправитель:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
namespace App\MessageSender;

use App\Message\ImportantAction;
use Symfony\Component\Messenger\Transport\SenderInterface;

class ImportantActionToEmailSender implements SenderInterface
{
   private $toEmail;
   private $mailer;

   public function __construct(\Swift_Mailer $mailer, string $toEmail)
   {
       $this->mailer = $mailer;
       $this->toEmail = $toEmail;
   }

   public function send($message)
   {
       if (!$message instanceof ImportantAction) {
           throw new \InvalidArgumentException(sprintf('Producer only supports "%s" messages.', ImportantAction::class));
       }

       $this->mailer->send(
           (new \Swift_Message('Important action made'))
               ->setTo($this->toEmail)
               ->setBody(
                   '<h1>Important action</h1><p>Made by '.$message->getUsername().'</p>',
                   'text/html'
               )
       );
   }
}

Ваш собственный получатель

Получатель отвечает за получение сообщений из источника и их диспетчеризацию приложению.

Представьте, что вы уже обработали какие-то "команды" в вашем приложении, используя сообщение NewOrder. Теперь вы хотите интегрироваться с третьей стороной или унаследованным приложением, но вы не можете использовать API и вам нужно использовать общедоступный CSV с новыми командами.

Вы прочтёте этот CSV файл и запустите сообщение NewOrder. Всё, что вам нужно сделать - это написать ваш пользовательский CSV получатель, а Symfony сделает всё остальное.

Для начала, создайте ваш собственный получатель:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
namespace App\MessageReceiver;

use App\Message\NewOrder;
use Symfony\Component\Messenger\Transport\ReceiverInterface;
use Symfony\Component\Serializer\SerializerInterface;

class NewOrdersFromCsvFile implements ReceiverInterface
{
   private $serializer;
   private $filePath;

   public function __construct(SerializerInterface $serializer, string $filePath)
   {
       $this->serializer = $serializer;
       $this->filePath = $filePath;
   }

   public function receive(callable $handler) : void
   {
       $ordersFromCsv = $this->serializer->deserialize(file_get_contents($this->filePath), 'csv');

       foreach ($ordersFromCsv as $orderFromCsv) {
           $handler(new NewOrder($orderFromCsv['id'], $orderFromCsv['account_id'], $orderFromCsv['amount']));
       }
   }

   public function stop(): void
   {
       // noop
   }
}

Получатель и отправитель в одном автобусе

Чтобы разрешить отправку и получение сообщений в одном и том же автобусе и предотвратить бесконечный цикл, автобус сообщений оснащён связующим ПО WrapIntoReceivedMessage. Оно обернёт полученные сообщения в объекты ReceivedMessage и связующее ПО SendMessageMiddleware будет знать, что ему не нужно повторно маршрутизировать эти сообщения к транспорту.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.