From 585491d1e6d9147a84041fe3cf1e44ddd452610d Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Wed, 14 Mar 2018 14:43:47 +0000 Subject: [PATCH 01/14] Move the documentation from the main PR to RST format. --- _images/components/message/overview.png | Bin 0 -> 22425 bytes components/message.rst | 235 ++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 _images/components/message/overview.png create mode 100644 components/message.rst diff --git a/_images/components/message/overview.png b/_images/components/message/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..074255b4667f5d05b0b158de6817decf15f8e086 GIT binary patch literal 22425 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 BliC0P literal 0 HcmV?d00001 diff --git a/components/message.rst b/components/message.rst new file mode 100644 index 00000000000..ebc6703fd85 --- /dev/null +++ b/components/message.rst @@ -0,0 +1,235 @@ +.. index:: + single: Message + single: Components; Message + +The Message Component +===================== + + The Message component helps application to send and receive messages + to/from other applications or via + +Concepts +-------- + +.. image:: /_images/components/message/overview.png + +1. **Sender** + Responsible for serializing and sending the message to _something_. This something can be a message broker or a 3rd + party API for example. + +2. **Receiver** + Responsible for deserializing and forwarding the messages to handler(s). This can be a message queue puller or an API + endpoint for example. + +3. **Handler** + Given a received message, contains the user business logic related to the message. In practice, that is just a PHP + callable. + +Bus +--- + +The bus is used to dispatch messages. MessageBus' behaviour is in its ordered middleware stack. When using +the message bus with Symfony's FrameworkBundle, the following middlewares are configured for you: + +1. `LoggingMiddleware` (log the processing of your messages) +2. `SendMessageMiddleware` (enable asynchronous processing) +3. `HandleMessageMiddleware` (call the registered handle) + + use App\Message\MyMessage; + + $result = $this->get('message_bus')->handle(new MyMessage(/* ... */)); + +Handlers +-------- + +Once dispatched to the bus, messages will be handled by a "message handler". A message handler is a PHP callable +(i.e. a function or an instance of a class) that will do the required processing for your message. It _might_ return a +result. + + namespace App\MessageHandler; + + use App\Message\MyMessage; + + class MyMessageHandler + { + public function __invoke(MyMessage $message) + { + // Message processing... + } + } + + + + + + +**Note:** If the message cannot be guessed from the handler's type-hint, use the `handles` attribute on the tag. + +### Asynchronous messages + +Using the Message Component is useful to decouple your application but it also very useful when you want to do some +asychronous processing. This means that your application will produce a message to a queuing system and consume this +message later in the background, using a _worker_. + +#### Adapters + +The communication with queuing system or 3rd parties is for delegated to libraries for now. You can use one of the +following adapters: + +- [PHP Enqueue bridge](https://github.com/sroze/enqueue-bridge) to use one of their 10+ compatible queues such as + RabbitMq, Amazon SQS or Google Pub/Sub. + +Routing +------- + +When doing asynchronous processing, the key is to route the message to the right sender. As the routing is +application-specific and not message-specific, the configuration can be made within the `framework.yaml` +configuration file as well: + + framework: + message: + routing: + 'My\Message\MessageAboutDoingOperationalWork': my_operations_queue_sender + +Such configuration would only route the `MessageAboutDoingOperationalWork` message to be asynchronous, the rest of the +messages would still be directly handled. + +If you want to do route all the messages to a queue by default, you can use such configuration: + + framework: + message: + routing: + 'My\Message\MessageAboutDoingOperationalWork': my_operations_queue_sender + '*': my_default_sender + +Note that you can also route a message to multiple senders at the same time: + + framework: + message: + routing: + 'My\Message\AnImportantMessage': [my_default_sender, my_audit_sender] + +Same bus received and sender +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To allow us to receive and send messages on the same bus and prevent a loop, the message bus is equipped with the +`WrapIntoReceivedMessage` received. It will wrap the received messages into `ReceivedMessage` objects and the +`SendMessageMiddleware` middleware will know it should not send these messages. + +Your own sender +--------------- + +Using the `SenderInterface`, you can easily create your own message sender. Let's say you already have an +`ImportantAction` message going through the message bus and handled by a handler. Now, you also want to send this +message as an email. + +1. Create your sender + + namespace App\MessageSender; + + use Symfony\Component\Message\SenderInterface; + use App\Message\ImportantAction; + + 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( + '

Important action

Made by '.$message->getUsername().'

', + 'text/html' + ) + ); + } + } + +2. Register your sender service + + services: + App\MessageSender\ImportantActionToEmailSender: + arguments: + - "@mailer" + - "%to_email%" + + tags: + - message.sender + +3. Route your important message to the sender + + framework: + message: + routing: + 'App\Message\ImportantAction': [App\MessageSender\ImportantActionToEmailSender, ~] + +**Note:** this example shows you how you can at the same time send your message and directly handle it using a `null` +(`~`) sender. + +Your own receiver +----------------- + +A consumer is responsible of receiving messages from a source and dispatching them to the application. + +Let's say you already proceed some "orders" on your application using a `NewOrder` message. Now you want to integrate with +a 3rd party or a legacy application but you can't use an API and need to use a shared CSV file with new orders. + +You will read this CSV file and dispatch a `NewOrder` message. All you need to do is your custom CSV consumer and Symfony will do the rest. + +1. Create your receiver + + namespace App\MessageReceiver; + + use Symfony\Component\Message\ReceiverInterface; + use Symfony\Component\Serializer\SerializerInterface; + + use App\Message\NewOrder; + + class NewOrdersFromCsvFile implements ReceiverInterface + { + private $serializer; + private $filePath; + + public function __construct(SerializerInteface $serializer, string $filePath) + { + $this->serializer = $serializer; + $this->filePath = $filePath; + } + + public function receive() : \Generator + { + $ordersFromCsv = $this->serializer->deserialize(file_get_contents($this->filePath), 'csv'); + + foreach ($ordersFromCsv as $orderFromCsv) { + yield new NewOrder($orderFromCsv['id'], $orderFromCsv['account_id'], $orderFromCsv['amount']); + } + } + } + +2. Register your receiver service + + services: + App\MessageReceiver\NewOrdersFromCsvFile: + arguments: + - "@serializer" + - "%new_orders_csv_file_path%" + + tags: + - message.receiver + +3. Use your consumer + + $ bin/console message:consume App\MessageReceived\NewOrdersFromCsvFile From b40bc7146c84647831d35f88e1d2cb6148af6240 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 18 Mar 2018 20:18:39 +0100 Subject: [PATCH 02/14] Fixed the RST syntax issues --- components/message.rst | 149 +++++++++++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 50 deletions(-) diff --git a/components/message.rst b/components/message.rst index ebc6703fd85..b9f05a306d6 100644 --- a/components/message.rst +++ b/components/message.rst @@ -8,32 +8,46 @@ The Message Component The Message component helps application to send and receive messages to/from other applications or via +Installation +------------ + +.. code-block:: terminal + + $ composer require symfony/message + +Alternatively, you can clone the ``_ repository. + +.. include:: /components/require_autoload.rst.inc + Concepts -------- .. image:: /_images/components/message/overview.png -1. **Sender** - Responsible for serializing and sending the message to _something_. This something can be a message broker or a 3rd - party API for example. +**Sender**: + Responsible for serializing and sending the message to _something_. This + something can be a message broker or a third party API for example. -2. **Receiver** - Responsible for deserializing and forwarding the messages to handler(s). This can be a message queue puller or an API - endpoint for example. +**Receiver**: + Responsible for deserializing and forwarding the messages to handler(s). This + can be a message queue puller or an API endpoint for example. -3. **Handler** - Given a received message, contains the user business logic related to the message. In practice, that is just a PHP - callable. +**Handler**: + Given a received message, contains the user business logic related to the + message. In practice, that is just a PHP callable. Bus --- -The bus is used to dispatch messages. MessageBus' behaviour is in its ordered middleware stack. When using -the message bus with Symfony's FrameworkBundle, the following middlewares are configured for you: +The bus is used to dispatch messages. MessageBus' behavior is in its ordered +middleware stack. When using the message bus with Symfony's FrameworkBundle, the +following middlewares are configured for you: + +#. ``LoggingMiddleware`` (logs the processing of your messages) +#. ``SendMessageMiddleware`` (enables asynchronous processing) +#. ``HandleMessageMiddleware`` (calls the registered handle) -1. `LoggingMiddleware` (log the processing of your messages) -2. `SendMessageMiddleware` (enable asynchronous processing) -3. `HandleMessageMiddleware` (call the registered handle) +Example:: use App\Message\MyMessage; @@ -42,9 +56,10 @@ the message bus with Symfony's FrameworkBundle, the following middlewares are co Handlers -------- -Once dispatched to the bus, messages will be handled by a "message handler". A message handler is a PHP callable -(i.e. a function or an instance of a class) that will do the required processing for your message. It _might_ return a -result. +Once dispatched to the bus, messages will be handled by a "message handler". A +message handler is a PHP callable (i.e. a function or an instance of a class) +that will do the required processing for your message. It _might_ return a +result:: namespace App\MessageHandler; @@ -58,43 +73,57 @@ result. } } +.. code-block:: xml -**Note:** If the message cannot be guessed from the handler's type-hint, use the `handles` attribute on the tag. +.. note:: -### Asynchronous messages + If the message cannot be guessed from the handler's type-hint, use the + ``handles`` attribute on the tag. -Using the Message Component is useful to decouple your application but it also very useful when you want to do some -asychronous processing. This means that your application will produce a message to a queuing system and consume this +Asynchronous messages +~~~~~~~~~~~~~~~~~~~~~ + +Using the Message Component is useful to decouple your application but it also +very useful when you want to do some asynchronous processing. This means that +your application will produce a message to a queuing system and consume this message later in the background, using a _worker_. -#### Adapters +Adapters +~~~~~~~~ -The communication with queuing system or 3rd parties is for delegated to libraries for now. You can use one of the -following adapters: +The communication with queuing system or third parties is for delegated to +libraries for now. You can use one of the following adapters: -- [PHP Enqueue bridge](https://github.com/sroze/enqueue-bridge) to use one of their 10+ compatible queues such as - RabbitMq, Amazon SQS or Google Pub/Sub. +#. `PHP Enqueue bridge`_ to use one of their 10+ compatible queues such as + RabbitMq, Amazon SQS or Google Pub/Sub. Routing ------- -When doing asynchronous processing, the key is to route the message to the right sender. As the routing is -application-specific and not message-specific, the configuration can be made within the `framework.yaml` -configuration file as well: +When doing asynchronous processing, the key is to route the message to the right +sender. As the routing is application-specific and not message-specific, the +configuration can be made within the ``framework.yaml`` configuration file as +well: + +.. code-block:: yaml framework: message: routing: 'My\Message\MessageAboutDoingOperationalWork': my_operations_queue_sender -Such configuration would only route the `MessageAboutDoingOperationalWork` message to be asynchronous, the rest of the -messages would still be directly handled. +Such configuration would only route the ``MessageAboutDoingOperationalWork`` +message to be asynchronous, the rest of the messages would still be directly +handled. -If you want to do route all the messages to a queue by default, you can use such configuration: +If you want to do route all the messages to a queue by default, you can use such +configuration: + +.. code-block:: yaml framework: message: @@ -104,6 +133,8 @@ If you want to do route all the messages to a queue by default, you can use such Note that you can also route a message to multiple senders at the same time: +.. code-block:: yaml + framework: message: routing: @@ -112,18 +143,20 @@ Note that you can also route a message to multiple senders at the same time: Same bus received and sender ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To allow us to receive and send messages on the same bus and prevent a loop, the message bus is equipped with the -`WrapIntoReceivedMessage` received. It will wrap the received messages into `ReceivedMessage` objects and the -`SendMessageMiddleware` middleware will know it should not send these messages. +To allow us to receive and send messages on the same bus and prevent a loop, the +message bus is equipped with the ``WrapIntoReceivedMessage`` received. It will +wrap the received messages into ``ReceivedMessage`` objects and the +``SendMessageMiddleware`` middleware will know it should not send these messages. Your own sender --------------- -Using the `SenderInterface`, you can easily create your own message sender. Let's say you already have an -`ImportantAction` message going through the message bus and handled by a handler. Now, you also want to send this -message as an email. +Using the ``SenderInterface``, you can easily create your own message sender. +Let's say you already have an ``ImportantAction`` message going through the +message bus and handled by a handler. Now, you also want to send this message as +an email. -1. Create your sender +First, create your sender:: namespace App\MessageSender; @@ -158,7 +191,9 @@ message as an email. } } -2. Register your sender service +Then, register your sender service: + +.. code-block:: yaml services: App\MessageSender\ImportantActionToEmailSender: @@ -169,27 +204,35 @@ message as an email. tags: - message.sender -3. Route your important message to the sender +Finally, route your important message to the sender: + +.. code-block:: yaml framework: message: routing: 'App\Message\ImportantAction': [App\MessageSender\ImportantActionToEmailSender, ~] -**Note:** this example shows you how you can at the same time send your message and directly handle it using a `null` -(`~`) sender. +.. note:: + + This example shows you how you can at the same time send your message and + directly handle it using a ``null`` (``~``) sender. Your own receiver ----------------- -A consumer is responsible of receiving messages from a source and dispatching them to the application. +A consumer is responsible of receiving messages from a source and dispatching +them to the application. -Let's say you already proceed some "orders" on your application using a `NewOrder` message. Now you want to integrate with -a 3rd party or a legacy application but you can't use an API and need to use a shared CSV file with new orders. +Let's say you already proceed some "orders" on your application using a +``NewOrder`` message. Now you want to integrate with a 3rd party or a legacy +application but you can't use an API and need to use a shared CSV file with new +orders. -You will read this CSV file and dispatch a `NewOrder` message. All you need to do is your custom CSV consumer and Symfony will do the rest. +You will read this CSV file and dispatch a ``NewOrder`` message. All you need to +do is your custom CSV consumer and Symfony will do the rest. -1. Create your receiver +First, create your receiver:: namespace App\MessageReceiver; @@ -219,7 +262,9 @@ You will read this CSV file and dispatch a `NewOrder` message. All you need to d } } -2. Register your receiver service +Then, register your receiver service: + +.. code-block:: yaml services: App\MessageReceiver\NewOrdersFromCsvFile: @@ -230,6 +275,10 @@ You will read this CSV file and dispatch a `NewOrder` message. All you need to d tags: - message.receiver -3. Use your consumer +Finally, use your consumer: + +.. code-block:: terminal $ bin/console message:consume App\MessageReceived\NewOrdersFromCsvFile + +.. _`PHP Enqueue bridge`: https://github.com/sroze/enqueue-bridge From b26de803dc46a876f9b378378ac1055485623e8a Mon Sep 17 00:00:00 2001 From: wiese Date: Fri, 23 Mar 2018 11:22:56 +0100 Subject: [PATCH 03/14] minor doc fixes for the Message component --- components/message.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/message.rst b/components/message.rst index b9f05a306d6..8e7c6700a8f 100644 --- a/components/message.rst +++ b/components/message.rst @@ -95,7 +95,7 @@ message later in the background, using a _worker_. Adapters ~~~~~~~~ -The communication with queuing system or third parties is for delegated to +The communication with queuing system or third parties is delegated to libraries for now. You can use one of the following adapters: #. `PHP Enqueue bridge`_ to use one of their 10+ compatible queues such as @@ -221,7 +221,7 @@ Finally, route your important message to the sender: Your own receiver ----------------- -A consumer is responsible of receiving messages from a source and dispatching +A consumer is responsible for receiving messages from a source and dispatching them to the application. Let's say you already proceed some "orders" on your application using a From 5c828e4dece1291de32e7fefb823734f52997ab2 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 27 Mar 2018 07:37:14 +0100 Subject: [PATCH 04/14] Update the documentation a bit, and add more FrameworkBundle documentation --- .../{message => messenger}/overview.png | Bin components/{message.rst => messenger.rst} | 162 ++++----------- messenger.rst | 186 ++++++++++++++++++ 3 files changed, 221 insertions(+), 127 deletions(-) rename _images/components/{message => messenger}/overview.png (100%) rename components/{message.rst => messenger.rst} (58%) create mode 100644 messenger.rst diff --git a/_images/components/message/overview.png b/_images/components/messenger/overview.png similarity index 100% rename from _images/components/message/overview.png rename to _images/components/messenger/overview.png diff --git a/components/message.rst b/components/messenger.rst similarity index 58% rename from components/message.rst rename to components/messenger.rst index 8e7c6700a8f..bab2131fa24 100644 --- a/components/message.rst +++ b/components/messenger.rst @@ -1,28 +1,27 @@ .. index:: - single: Message - single: Components; Message + single: Messenger + single: Components; Messenger -The Message Component -===================== +The Messenger Component +======================= - The Message component helps application to send and receive messages - to/from other applications or via + The Messenger component helps application send and receive messages to/from other applications or via message queues. Installation ------------ .. code-block:: terminal - $ composer require symfony/message + $ composer require symfony/messenger -Alternatively, you can clone the ``_ repository. +Alternatively, you can clone the ``_ repository. .. include:: /components/require_autoload.rst.inc Concepts -------- -.. image:: /_images/components/message/overview.png +.. image:: /_images/components/messenger/overview.png **Sender**: Responsible for serializing and sending the message to _something_. This @@ -47,11 +46,20 @@ following middlewares are configured for you: #. ``SendMessageMiddleware`` (enables asynchronous processing) #. ``HandleMessageMiddleware`` (calls the registered handle) -Example:: +Example: use App\Message\MyMessage; + use Symfony\Component\Messenger\MessageBus; + use Symfony\Component\Messenger\HandlerLocator; + use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; - $result = $this->get('message_bus')->handle(new MyMessage(/* ... */)); + $bus = new MessageBus([ + new HandleMessageMiddleware(new HandlerLocator([ + MyMessage::class => $handler, + ])) + ]); + + $result = $bus->handle(new MyMessage(/* ... */)); Handlers -------- @@ -59,7 +67,7 @@ Handlers Once dispatched to the bus, messages will be handled by a "message handler". A message handler is a PHP callable (i.e. a function or an instance of a class) that will do the required processing for your message. It _might_ return a -result:: +result: namespace App\MessageHandler; @@ -73,80 +81,14 @@ result:: } } -.. code-block:: xml - - - - - -.. note:: - - If the message cannot be guessed from the handler's type-hint, use the - ``handles`` attribute on the tag. - -Asynchronous messages -~~~~~~~~~~~~~~~~~~~~~ - -Using the Message Component is useful to decouple your application but it also -very useful when you want to do some asynchronous processing. This means that -your application will produce a message to a queuing system and consume this -message later in the background, using a _worker_. - Adapters -~~~~~~~~ +-------- The communication with queuing system or third parties is delegated to -libraries for now. You can use one of the following adapters: - -#. `PHP Enqueue bridge`_ to use one of their 10+ compatible queues such as - RabbitMq, Amazon SQS or Google Pub/Sub. - -Routing -------- - -When doing asynchronous processing, the key is to route the message to the right -sender. As the routing is application-specific and not message-specific, the -configuration can be made within the ``framework.yaml`` configuration file as -well: - -.. code-block:: yaml - - framework: - message: - routing: - 'My\Message\MessageAboutDoingOperationalWork': my_operations_queue_sender +libraries for now. -Such configuration would only route the ``MessageAboutDoingOperationalWork`` -message to be asynchronous, the rest of the messages would still be directly -handled. - -If you want to do route all the messages to a queue by default, you can use such -configuration: - -.. code-block:: yaml - - framework: - message: - routing: - 'My\Message\MessageAboutDoingOperationalWork': my_operations_queue_sender - '*': my_default_sender - -Note that you can also route a message to multiple senders at the same time: - -.. code-block:: yaml - - framework: - message: - routing: - 'My\Message\AnImportantMessage': [my_default_sender, my_audit_sender] - -Same bus received and sender -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To allow us to receive and send messages on the same bus and prevent a loop, the -message bus is equipped with the ``WrapIntoReceivedMessage`` received. It will -wrap the received messages into ``ReceivedMessage`` objects and the -``SendMessageMiddleware`` middleware will know it should not send these messages. +Create your adapter +~~~~~~~~~~~~~~~~~~~ Your own sender --------------- @@ -160,8 +102,8 @@ First, create your sender:: namespace App\MessageSender; - use Symfony\Component\Message\SenderInterface; use App\Message\ImportantAction; + use Symfony\Component\Message\SenderInterface; class ImportantActionToEmailSender implements SenderInterface { @@ -191,33 +133,6 @@ First, create your sender:: } } -Then, register your sender service: - -.. code-block:: yaml - - services: - App\MessageSender\ImportantActionToEmailSender: - arguments: - - "@mailer" - - "%to_email%" - - tags: - - message.sender - -Finally, route your important message to the sender: - -.. code-block:: yaml - - framework: - message: - routing: - 'App\Message\ImportantAction': [App\MessageSender\ImportantActionToEmailSender, ~] - -.. note:: - - This example shows you how you can at the same time send your message and - directly handle it using a ``null`` (``~``) sender. - Your own receiver ----------------- @@ -236,11 +151,10 @@ First, create your receiver:: namespace App\MessageReceiver; + use App\Message\NewOrder; use Symfony\Component\Message\ReceiverInterface; use Symfony\Component\Serializer\SerializerInterface; - use App\Message\NewOrder; - class NewOrdersFromCsvFile implements ReceiverInterface { private $serializer; @@ -262,23 +176,17 @@ First, create your receiver:: } } -Then, register your receiver service: - -.. code-block:: yaml +Your adapter factory +~~~~~~~~~~~~~~~~~~~~ - services: - App\MessageReceiver\NewOrdersFromCsvFile: - arguments: - - "@serializer" - - "%new_orders_csv_file_path%" +TODO. - tags: - - message.receiver - -Finally, use your consumer: - -.. code-block:: terminal +Same bus received and sender +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - $ bin/console message:consume App\MessageReceived\NewOrdersFromCsvFile +To allow us to receive and send messages on the same bus and prevent a loop, the +message bus is equipped with the ``WrapIntoReceivedMessage`` received. It will +wrap the received messages into ``ReceivedMessage`` objects and the +``SendMessageMiddleware`` middleware will know it should not send these messages. .. _`PHP Enqueue bridge`: https://github.com/sroze/enqueue-bridge diff --git a/messenger.rst b/messenger.rst new file mode 100644 index 00000000000..15bf1b3e32e --- /dev/null +++ b/messenger.rst @@ -0,0 +1,186 @@ +.. index:: + single: Messenger + +How to Use the Messenger +======================== + +Symfony's Messenger provide a message bus and some routing capabilities to send +messages within your application and through adapaters such as message queues. +Before using it, read the :doc:`Messenger component docs ` +to get familiar with its concepts. + +Installation +------------ + +In applications using :doc:`Symfony Flex `, run this command to +install messenger before using it: + +.. code-block:: terminal + + $ composer require messenger + +Using the Messenger Service +--------------------------- + +Once enabled, the :code:`message_bus` service can be injected in any service where +you need it, like in a controller:: + + // src/Controller/DefaultController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use Symfony\Component\Messenger\MessageBusInterface; + + class DefaultController extends Controller + { + public function indexAction(MessageBusInterface $bus) + { + $bus->dispatch(new MyMessage()); + } + } + +Registering Handlers +-------------------- + +In order to do something when your message is dispatched, you need to create a +message handler. It's a class with an `__invoke` method: + + // src/MessageHandler/MyMessageHandler.php + namespace App\MessageHandler; + + class MyMessageHandler + { + public function __invoke(MyMessage $message) + { + // do something with it. + } + } + +Once you've created your handler, you need to register it: + +.. code-block:: xml + + + + + +.. note:: + + If the message cannot be guessed from the handler's type-hint, use the + ``handles`` attribute on the tag. + +Adapters +-------- + +The communication with queuing system or third parties is delegated to +libraries for now. The `built-in AMQP adapter`_ allows you to communicate with +most of the AMQP brokers such as RabbitMQ. + +.. note:: + + If you need more message brokers, you should have a look to Enqueue's adapter + which supports things like Kafka, Amazon SQS or Google Pub/Sub. + +An adapter is registered using a "DSN", which is a string that represents the +connection credentials and configuration. By default, when you've installed +the messenger component, the following configuration should have been created: + +.. code-block:: yaml + + # config/packages/messenger.yaml + framework: + messenger: + adapters: + default: "%env(MESSENGER_DSN)%" + +.. code-block:: env + + # .env + ###> symfony/messenger ### + AMQP_DSN=amqp://guest:guest@localhost:5672/%2f/messages + ###< symfony/messenger ### + +This is enough to allow you to route your message to the :code:`messenger.default_adapter` +adapter. This will also configure the following for you: + +1. A :code:`messenger.default_sender` sender to be used when routing messages +2. A :code:`messenger.default_receiver` receiver to be used when consuming messages. + +Routing +------- + +Instead of calling a handler, you have the option to route your message(s) to a +sender. Part of an adapter, it is responsible of sending your message somewhere. +You can configuration which message is routed to which sender with the following +configuration: + +.. code-block:: yaml + + framework: + messenger: + routing: + 'My\Message\Message': messenger.default_sender # Or another sender service name + +Such configuration would only route the ``MessageAboutDoingOperationalWork`` +message to be asynchronous, the rest of the messages would still be directly +handled. + +If you want to do route all the messages to a queue by default, you can use such +configuration: + +.. code-block:: yaml + + framework: + messenger: + routing: + 'My\Message\MessageAboutDoingOperationalWork': messenger.operations_sender + '*': messenger.default_sender + +Note that you can also route a message to multiple senders at the same time: + +.. code-block:: yaml + + framework: + messenger: + routing: + 'My\Message\ToBeSentToTwoSenders': [messenger.default_sender, messenger.audit_sender] + +Last but not least you can also route a message while still calling the handler +on your application by having a :code:`null` sender: + +.. code-block:: yaml + + framework: + messenger: + routing: + 'My\Message\ThatIsGoingToBeSentAndHandledLocally': [messenger.default_sender, ~] + +Consuming messages +------------------ + +Once your messages have been routed, you will like to consume your messages in most +of the cases. Do to so, you can use the :code:`messenger:consume-messages` command +like this: + +.. code-block:: terminal + + $ bin/console messenger:consume-messages messenger.default_receiver + +The first argument is the receiver's service name. It might have been created by +your :code:`adapaters` configuration or it can be your own receiver. + +Your own Adapters +----------------- + +Learn how to build your own adapters within the Component's documentation. Once +you have built your classes, you can register your adapater to be able to use +it via a DSN in the Symfony application. + +Register your factory +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: xml + + + + From 31a56ee081c24985e74af7b6c7abcb6f9d6759aa Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 27 Mar 2018 11:25:17 +0100 Subject: [PATCH 05/14] Uses `::` to display as code blocks --- components/messenger.rst | 4 ++-- messenger.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/messenger.rst b/components/messenger.rst index bab2131fa24..f28b9af946d 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -46,7 +46,7 @@ following middlewares are configured for you: #. ``SendMessageMiddleware`` (enables asynchronous processing) #. ``HandleMessageMiddleware`` (calls the registered handle) -Example: +Example:: use App\Message\MyMessage; use Symfony\Component\Messenger\MessageBus; @@ -67,7 +67,7 @@ Handlers Once dispatched to the bus, messages will be handled by a "message handler". A message handler is a PHP callable (i.e. a function or an instance of a class) that will do the required processing for your message. It _might_ return a -result: +result:: namespace App\MessageHandler; diff --git a/messenger.rst b/messenger.rst index 15bf1b3e32e..a72f693e612 100644 --- a/messenger.rst +++ b/messenger.rst @@ -43,7 +43,7 @@ Registering Handlers -------------------- In order to do something when your message is dispatched, you need to create a -message handler. It's a class with an `__invoke` method: +message handler. It's a class with an `__invoke` method:: // src/MessageHandler/MyMessageHandler.php namespace App\MessageHandler; From 2493c9087cf203134ae8112b2878c4537a73842c Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 27 Mar 2018 13:55:15 +0100 Subject: [PATCH 06/14] Update typos and missing reference --- components/messenger.rst | 13 ++++--------- messenger.rst | 10 ++++++---- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/components/messenger.rst b/components/messenger.rst index f28b9af946d..9fa828abe9f 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -38,13 +38,13 @@ Concepts Bus --- -The bus is used to dispatch messages. MessageBus' behavior is in its ordered +The bus is used to dispatch messages. MessageBus' behaviour is in its ordered middleware stack. When using the message bus with Symfony's FrameworkBundle, the following middlewares are configured for you: -#. ``LoggingMiddleware`` (logs the processing of your messages) -#. ``SendMessageMiddleware`` (enables asynchronous processing) -#. ``HandleMessageMiddleware`` (calls the registered handle) +#. :code:`LoggingMiddleware` (logs the processing of your messages) +#. :code:`SendMessageMiddleware` (enables asynchronous processing) +#. :code:`HandleMessageMiddleware` (calls the registered handle) Example:: @@ -176,11 +176,6 @@ First, create your receiver:: } } -Your adapter factory -~~~~~~~~~~~~~~~~~~~~ - -TODO. - Same bus received and sender ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/messenger.rst b/messenger.rst index a72f693e612..00f82a75e1e 100644 --- a/messenger.rst +++ b/messenger.rst @@ -73,7 +73,7 @@ Adapters -------- The communication with queuing system or third parties is delegated to -libraries for now. The `built-in AMQP adapter`_ allows you to communicate with +libraries for now. The built-in AMQP adapter allows you to communicate with most of the AMQP brokers such as RabbitMQ. .. note:: @@ -167,14 +167,16 @@ like this: $ bin/console messenger:consume-messages messenger.default_receiver The first argument is the receiver's service name. It might have been created by -your :code:`adapaters` configuration or it can be your own receiver. +your :code:`adapters` configuration or it can be your own receiver. Your own Adapters ----------------- Learn how to build your own adapters within the Component's documentation. Once -you have built your classes, you can register your adapater to be able to use -it via a DSN in the Symfony application. +you have built your classes, you can register your adapter factory to be able to +use it via a DSN in the Symfony application. + + Register your factory ~~~~~~~~~~~~~~~~~~~~~ From bcfae23b07559cd61bd40929f472c78e9594e70f Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 27 Mar 2018 14:28:37 +0100 Subject: [PATCH 07/14] Finish the documentation about the custom adapter --- components/messenger.rst | 17 +++++------ messenger.rst | 65 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/components/messenger.rst b/components/messenger.rst index 9fa828abe9f..6c6a29d937b 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -136,16 +136,16 @@ First, create your sender:: Your own receiver ----------------- -A consumer is responsible for receiving messages from a source and dispatching +A receiver is responsible for receiving messages from a source and dispatching them to the application. -Let's say you already proceed some "orders" on your application using a +Let's say you already processed some "orders" in your application using a ``NewOrder`` message. Now you want to integrate with a 3rd party or a legacy application but you can't use an API and need to use a shared CSV file with new orders. You will read this CSV file and dispatch a ``NewOrder`` message. All you need to -do is your custom CSV consumer and Symfony will do the rest. +do is to write your custom CSV receiver and Symfony will do the rest. First, create your receiver:: @@ -179,9 +179,8 @@ First, create your receiver:: Same bus received and sender ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To allow us to receive and send messages on the same bus and prevent a loop, the -message bus is equipped with the ``WrapIntoReceivedMessage`` received. It will -wrap the received messages into ``ReceivedMessage`` objects and the -``SendMessageMiddleware`` middleware will know it should not send these messages. - -.. _`PHP Enqueue bridge`: https://github.com/sroze/enqueue-bridge +To allow us to receive and send messages on the same bus and prevent an infinite +loop, the message bus is equipped with the :code:`WrapIntoReceivedMessage` middleware. +It will wrap the received messages into :code:`ReceivedMessage` objects and the +:code:`SendMessageMiddleware` middleware will know it should not route these +messages again to an adapter. diff --git a/messenger.rst b/messenger.rst index 00f82a75e1e..db81055ed8c 100644 --- a/messenger.rst +++ b/messenger.rst @@ -61,7 +61,7 @@ Once you've created your handler, you need to register it: .. code-block:: xml - + .. note:: @@ -78,7 +78,7 @@ most of the AMQP brokers such as RabbitMQ. .. note:: - If you need more message brokers, you should have a look to Enqueue's adapter + If you need more message brokers, you should have a look to `Enqueue's adapter`_ which supports things like Kafka, Amazon SQS or Google Pub/Sub. An adapter is registered using a "DSN", which is a string that represents the @@ -176,7 +176,47 @@ Learn how to build your own adapters within the Component's documentation. Once you have built your classes, you can register your adapter factory to be able to use it via a DSN in the Symfony application. +Create your adapter Factory +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You need to give FrameworkBundle the opportunity to create your adapter from a +DSN. You will need an adapter factory:: + + use Symfony\Component\Messenger\Adapter\Factory\AdapterInterface; + use Symfony\Component\Messenger\Adapter\Factory\AdapterFactoryInterface; + + class YourAdapterFactory implements AdapterFactoryInterface + { + public function create(string $dsn): AdapterInterface + { + return new YourAdapter(/* ... */); + } + + public function supports(string $dsn): bool + { + return 0 === strpos($dsn, 'my-adapter://'); + } + } + +The :code:`YourAdaper` class need to implements the :code:`AdapterInterface`. It +will like the following example:: + + use Symfony\Component\Messenger\Adapter\Factory\AdapterInterface; + use Symfony\Component\Messenger\Transport\ReceiverInterface; + use Symfony\Component\Messenger\Transport\SenderInterface; + + class YourAdapter implements AdapterInterface + { + public function receiver(): ReceiverInterface + { + return new YourReceiver(/* ... */); + } + + public function sender(): SenderInterface + { + return new YourSender(/* ... */); + } + } Register your factory ~~~~~~~~~~~~~~~~~~~~~ @@ -186,3 +226,24 @@ Register your factory + +Use your adapter +~~~~~~~~~~~~~~~~ + +Within the :code:`framework.messenger.adapters.*` configuration, create your +named adapter using your own DSN: + +.. code-block:: yaml + + framework: + messenger: + adapters: + yours: 'my-adapter://...' + +This will give you access to the following services: + +1. :code:`messenger.yours_adapter`: the instance of your adapter. +2. :code:`messenger.yours_receiver` and :code:`messenger.yours_sender`, the + receiver and sender created by the adapter. + +.. _`PHP Enqueue bridge`: https://github.com/sroze/enqueue-bridge From 25c0b6ec72011d74994ac7cb9d98817f122d5368 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 27 Mar 2018 14:29:20 +0100 Subject: [PATCH 08/14] It helps multiple applications Please enter the commit message for your changes. Lines starting --- components/messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/messenger.rst b/components/messenger.rst index 6c6a29d937b..2eb5090ec13 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -5,7 +5,7 @@ The Messenger Component ======================= - The Messenger component helps application send and receive messages to/from other applications or via message queues. + The Messenger component helps applications send and receive messages to/from other applications or via message queues. Installation ------------ From a15752bad649252c8b6d5230eda4800af587ebbc Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 27 Mar 2018 14:51:35 +0100 Subject: [PATCH 09/14] Fix unresolved reference --- messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index db81055ed8c..1d5b7b4ef35 100644 --- a/messenger.rst +++ b/messenger.rst @@ -246,4 +246,4 @@ This will give you access to the following services: 2. :code:`messenger.yours_receiver` and :code:`messenger.yours_sender`, the receiver and sender created by the adapter. -.. _`PHP Enqueue bridge`: https://github.com/sroze/enqueue-bridge +.. _`enqueue's adapter`: https://github.com/sroze/enqueue-bridge From fb88abce74ae90b158f3b93912a368efd0c8e966 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 27 Mar 2018 14:58:40 +0100 Subject: [PATCH 10/14] Add Messenger in the topics --- index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/index.rst b/index.rst index b9e4a8d316b..ab5212eff54 100644 --- a/index.rst +++ b/index.rst @@ -41,6 +41,7 @@ Topics frontend http_cache logging + messenger performance profiler routing From 509e1494af904a4c79fb73936d2062c55b9361ee Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 27 Mar 2018 15:13:43 +0100 Subject: [PATCH 11/14] Add a documentation about middlewares and update based on reviews --- components/messenger.rst | 21 ++++++++++++--------- messenger.rst | 17 ++++++++++++++++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/components/messenger.rst b/components/messenger.rst index 2eb5090ec13..cbbc62d3c72 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -38,9 +38,11 @@ Concepts Bus --- -The bus is used to dispatch messages. MessageBus' behaviour is in its ordered -middleware stack. When using the message bus with Symfony's FrameworkBundle, the -following middlewares are configured for you: +The bus is used to dispatch messages. The behaviour of the bus is in its ordered +middleware stack. The component comes with a set of middlewares that you can use. + +When using the message bus with Symfony's FrameworkBundle, the following middlewares +are configured for you: #. :code:`LoggingMiddleware` (logs the processing of your messages) #. :code:`SendMessageMiddleware` (enables asynchronous processing) @@ -56,11 +58,15 @@ Example:: $bus = new MessageBus([ new HandleMessageMiddleware(new HandlerLocator([ MyMessage::class => $handler, - ])) + ])), ]); $result = $bus->handle(new MyMessage(/* ... */)); +.. note: + + Every middleware need to implement the :code:`MiddlewareInterface` interface. + Handlers -------- @@ -87,11 +93,8 @@ Adapters The communication with queuing system or third parties is delegated to libraries for now. -Create your adapter -~~~~~~~~~~~~~~~~~~~ - Your own sender ---------------- +~~~~~~~~~~~~~~~ Using the ``SenderInterface``, you can easily create your own message sender. Let's say you already have an ``ImportantAction`` message going through the @@ -134,7 +137,7 @@ First, create your sender:: } Your own receiver ------------------ +~~~~~~~~~~~~~~~~~ A receiver is responsible for receiving messages from a source and dispatching them to the application. diff --git a/messenger.rst b/messenger.rst index 1d5b7b4ef35..8efdcad6ebe 100644 --- a/messenger.rst +++ b/messenger.rst @@ -93,7 +93,7 @@ the messenger component, the following configuration should have been created: adapters: default: "%env(MESSENGER_DSN)%" -.. code-block:: env +.. code-block:: bash # .env ###> symfony/messenger ### @@ -169,6 +169,21 @@ like this: The first argument is the receiver's service name. It might have been created by your :code:`adapters` configuration or it can be your own receiver. +Registering your middlewares +---------------------------- + +The message bus is based on middlewares. If you are un-familiar with the concept, +look at the :doc:`Messenger component docs `. + +To register your middleware, use the :code:`messenger.middleware` tag as in the +following example: + +.. code-block:: xml + + + + + Your own Adapters ----------------- From 3fb973c2612914e0dafcf2f1db16861d502aa3ae Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Thu, 29 Mar 2018 09:14:39 +0200 Subject: [PATCH 12/14] Update based on comments --- components/messenger.rst | 18 ++++++++--------- messenger.rst | 43 ++++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/components/messenger.rst b/components/messenger.rst index cbbc62d3c72..1bf5f6a5bf8 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -39,14 +39,14 @@ Bus --- The bus is used to dispatch messages. The behaviour of the bus is in its ordered -middleware stack. The component comes with a set of middlewares that you can use. +middleware stack. The component comes with a set of middleware that you can use. -When using the message bus with Symfony's FrameworkBundle, the following middlewares +When using the message bus with Symfony's FrameworkBundle, the following middleware are configured for you: -#. :code:`LoggingMiddleware` (logs the processing of your messages) -#. :code:`SendMessageMiddleware` (enables asynchronous processing) -#. :code:`HandleMessageMiddleware` (calls the registered handle) +#. ``LoggingMiddleware`` (logs the processing of your messages) +#. ``SendMessageMiddleware`` (enables asynchronous processing) +#. ``HandleMessageMiddleware`` (calls the registered handle) Example:: @@ -65,7 +65,7 @@ Example:: .. note: - Every middleware need to implement the :code:`MiddlewareInterface` interface. + Every middleware need to implement the ``MiddlewareInterface`` interface. Handlers -------- @@ -183,7 +183,7 @@ Same bus received and sender ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To allow us to receive and send messages on the same bus and prevent an infinite -loop, the message bus is equipped with the :code:`WrapIntoReceivedMessage` middleware. -It will wrap the received messages into :code:`ReceivedMessage` objects and the -:code:`SendMessageMiddleware` middleware will know it should not route these +loop, the message bus is equipped with the ``WrapIntoReceivedMessage`` middleware. +It will wrap the received messages into ``ReceivedMessage`` objects and the +``SendMessageMiddleware`` middleware will know it should not route these messages again to an adapter. diff --git a/messenger.rst b/messenger.rst index 8efdcad6ebe..bb68744baa7 100644 --- a/messenger.rst +++ b/messenger.rst @@ -22,7 +22,7 @@ install messenger before using it: Using the Messenger Service --------------------------- -Once enabled, the :code:`message_bus` service can be injected in any service where +Once enabled, the ``message_bus`` service can be injected in any service where you need it, like in a controller:: // src/Controller/DefaultController.php @@ -33,9 +33,9 @@ you need it, like in a controller:: class DefaultController extends Controller { - public function indexAction(MessageBusInterface $bus) + public function index(MessageBusInterface $bus) { - $bus->dispatch(new MyMessage()); + $bus->dispatch(new SendNotification('A string to be sent...')); } } @@ -97,21 +97,21 @@ the messenger component, the following configuration should have been created: # .env ###> symfony/messenger ### - AMQP_DSN=amqp://guest:guest@localhost:5672/%2f/messages + MESSENGER_DSN=amqp://guest:guest@localhost:5672/%2f/messages ###< symfony/messenger ### -This is enough to allow you to route your message to the :code:`messenger.default_adapter` +This is enough to allow you to route your message to the ``messenger.default_adapter`` adapter. This will also configure the following for you: -1. A :code:`messenger.default_sender` sender to be used when routing messages -2. A :code:`messenger.default_receiver` receiver to be used when consuming messages. +1. A ``messenger.default_sender`` sender to be used when routing messages +2. A ``messenger.default_receiver`` receiver to be used when consuming messages. Routing ------- Instead of calling a handler, you have the option to route your message(s) to a sender. Part of an adapter, it is responsible of sending your message somewhere. -You can configuration which message is routed to which sender with the following +You can configure which message is routed to which sender with the following configuration: .. code-block:: yaml @@ -121,9 +121,8 @@ configuration: routing: 'My\Message\Message': messenger.default_sender # Or another sender service name -Such configuration would only route the ``MessageAboutDoingOperationalWork`` -message to be asynchronous, the rest of the messages would still be directly -handled. +Such configuration would only route the ``My\Message\Message`` message to be +asynchronous, the rest of the messages would still be directly handled. If you want to do route all the messages to a queue by default, you can use such configuration: @@ -146,7 +145,7 @@ Note that you can also route a message to multiple senders at the same time: 'My\Message\ToBeSentToTwoSenders': [messenger.default_sender, messenger.audit_sender] Last but not least you can also route a message while still calling the handler -on your application by having a :code:`null` sender: +on your application by having a ``null`` sender: .. code-block:: yaml @@ -159,7 +158,7 @@ Consuming messages ------------------ Once your messages have been routed, you will like to consume your messages in most -of the cases. Do to so, you can use the :code:`messenger:consume-messages` command +of the cases. Do to so, you can use the ``messenger:consume-messages`` command like this: .. code-block:: terminal @@ -167,15 +166,15 @@ like this: $ bin/console messenger:consume-messages messenger.default_receiver The first argument is the receiver's service name. It might have been created by -your :code:`adapters` configuration or it can be your own receiver. +your ``adapters`` configuration or it can be your own receiver. -Registering your middlewares ----------------------------- +Registering your middleware +--------------------------- -The message bus is based on middlewares. If you are un-familiar with the concept, +The message bus is based on a set of middleware. If you are un-familiar with the concept, look at the :doc:`Messenger component docs `. -To register your middleware, use the :code:`messenger.middleware` tag as in the +To register your middleware, use the ``messenger.middleware`` tag as in the following example: .. code-block:: xml @@ -213,7 +212,7 @@ DSN. You will need an adapter factory:: } } -The :code:`YourAdaper` class need to implements the :code:`AdapterInterface`. It +The ``YourAdaper`` class need to implement the ``AdapterInterface``. It will like the following example:: use Symfony\Component\Messenger\Adapter\Factory\AdapterInterface; @@ -245,7 +244,7 @@ Register your factory Use your adapter ~~~~~~~~~~~~~~~~ -Within the :code:`framework.messenger.adapters.*` configuration, create your +Within the ``framework.messenger.adapters.*`` configuration, create your named adapter using your own DSN: .. code-block:: yaml @@ -257,8 +256,8 @@ named adapter using your own DSN: This will give you access to the following services: -1. :code:`messenger.yours_adapter`: the instance of your adapter. -2. :code:`messenger.yours_receiver` and :code:`messenger.yours_sender`, the +1. ``messenger.yours_adapter``: the instance of your adapter. +2. ``messenger.yours_receiver`` and ``messenger.yours_sender``, the receiver and sender created by the adapter. .. _`enqueue's adapter`: https://github.com/sroze/enqueue-bridge From 32403eac9bdf6cb9f8a23440715d121276eee12d Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Thu, 12 Apr 2018 21:13:01 +0100 Subject: [PATCH 13/14] Update the documentation to reflect the latest changes --- components/messenger.rst | 29 +++++++------ messenger.rst | 94 ++++++++++++++-------------------------- 2 files changed, 48 insertions(+), 75 deletions(-) diff --git a/components/messenger.rst b/components/messenger.rst index 1bf5f6a5bf8..31f73bf8c37 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -24,16 +24,15 @@ Concepts .. image:: /_images/components/messenger/overview.png **Sender**: - Responsible for serializing and sending the message to _something_. This + Responsible for serializing and sending messages to _something_. This something can be a message broker or a third party API for example. **Receiver**: - Responsible for deserializing and forwarding the messages to handler(s). This + Responsible for deserializing and forwarding messages to handler(s). This can be a message queue puller or an API endpoint for example. **Handler**: - Given a received message, contains the user business logic related to the - message. In practice, that is just a PHP callable. + Responsible for handling messages using the business logic applicable to the messages. Bus --- @@ -65,15 +64,14 @@ Example:: .. note: - Every middleware need to implement the ``MiddlewareInterface`` interface. + Every middleware needs to implement the ``MiddlewareInterface`` interface. Handlers -------- Once dispatched to the bus, messages will be handled by a "message handler". A message handler is a PHP callable (i.e. a function or an instance of a class) -that will do the required processing for your message. It _might_ return a -result:: +that will do the required processing for your message:: namespace App\MessageHandler; @@ -90,8 +88,8 @@ result:: Adapters -------- -The communication with queuing system or third parties is delegated to -libraries for now. +In order to send and receive messages, you will have to configure an adapter. An +adapter will be responsible of communicating with your message broker or 3rd parties. Your own sender ~~~~~~~~~~~~~~~ @@ -169,18 +167,23 @@ First, create your receiver:: $this->filePath = $filePath; } - public function receive() : \Generator + public function receive(callable $handler) : void { $ordersFromCsv = $this->serializer->deserialize(file_get_contents($this->filePath), 'csv'); foreach ($ordersFromCsv as $orderFromCsv) { - yield new NewOrder($orderFromCsv['id'], $orderFromCsv['account_id'], $orderFromCsv['amount']); + $handler(new NewOrder($orderFromCsv['id'], $orderFromCsv['account_id'], $orderFromCsv['amount'])); } } + + public function stop(): void + { + // noop + } } -Same bus received and sender -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Receiver and Sender on the same bus +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To allow us to receive and send messages on the same bus and prevent an infinite loop, the message bus is equipped with the ``WrapIntoReceivedMessage`` middleware. diff --git a/messenger.rst b/messenger.rst index bb68744baa7..f6d271eed62 100644 --- a/messenger.rst +++ b/messenger.rst @@ -28,6 +28,7 @@ you need it, like in a controller:: // src/Controller/DefaultController.php namespace App\Controller; + use App\Message\SendNotification; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Messenger\MessageBusInterface; @@ -91,7 +92,7 @@ the messenger component, the following configuration should have been created: framework: messenger: adapters: - default: "%env(MESSENGER_DSN)%" + amqp: "%env(MESSENGER_DSN)%" .. code-block:: bash @@ -100,17 +101,17 @@ the messenger component, the following configuration should have been created: MESSENGER_DSN=amqp://guest:guest@localhost:5672/%2f/messages ###< symfony/messenger ### -This is enough to allow you to route your message to the ``messenger.default_adapter`` -adapter. This will also configure the following for you: +This is enough to allow you to route your message to the ``amqp``. This will also +configure the following services for you: -1. A ``messenger.default_sender`` sender to be used when routing messages -2. A ``messenger.default_receiver`` receiver to be used when consuming messages. +1. A ``messenger.sender.amqp`` sender to be used when routing messages. +2. A ``messenger.receiver.amqp`` receiver to be used when consuming messages. Routing ------- Instead of calling a handler, you have the option to route your message(s) to a -sender. Part of an adapter, it is responsible of sending your message somewhere. +sender. Part of an adapter, it is responsible for sending your message somewhere. You can configure which message is routed to which sender with the following configuration: @@ -119,40 +120,39 @@ configuration: framework: messenger: routing: - 'My\Message\Message': messenger.default_sender # Or another sender service name + 'My\Message\Message': amqp # The name of the defined adapter Such configuration would only route the ``My\Message\Message`` message to be asynchronous, the rest of the messages would still be directly handled. -If you want to do route all the messages to a queue by default, you can use such -configuration: +You can route all classes of message to a sender using an asterisk instead of a class name: .. code-block:: yaml framework: messenger: routing: - 'My\Message\MessageAboutDoingOperationalWork': messenger.operations_sender - '*': messenger.default_sender + 'My\Message\MessageAboutDoingOperationalWork': another_adapter + '*': amqp -Note that you can also route a message to multiple senders at the same time: +A class of message can also be routed to a multiple senders by specifying a list: .. code-block:: yaml framework: messenger: routing: - 'My\Message\ToBeSentToTwoSenders': [messenger.default_sender, messenger.audit_sender] + 'My\Message\ToBeSentToTwoSenders': [amqp, audit] -Last but not least you can also route a message while still calling the handler -on your application by having a ``null`` sender: +By specifying a ``null`` sender, you can also route a class of messages to a sender +while still having them passed to their respective handler: .. code-block:: yaml framework: messenger: routing: - 'My\Message\ThatIsGoingToBeSentAndHandledLocally': [messenger.default_sender, ~] + 'My\Message\ThatIsGoingToBeSentAndHandledLocally': [amqp, ~] Consuming messages ------------------ @@ -163,32 +163,16 @@ like this: .. code-block:: terminal - $ bin/console messenger:consume-messages messenger.default_receiver + $ bin/console messenger:consume-messages amqp The first argument is the receiver's service name. It might have been created by your ``adapters`` configuration or it can be your own receiver. -Registering your middleware ---------------------------- - -The message bus is based on a set of middleware. If you are un-familiar with the concept, -look at the :doc:`Messenger component docs `. - -To register your middleware, use the ``messenger.middleware`` tag as in the -following example: - -.. code-block:: xml - - - - - Your own Adapters ----------------- -Learn how to build your own adapters within the Component's documentation. Once -you have built your classes, you can register your adapter factory to be able to -use it via a DSN in the Symfony application. +Once you have written your adapter's sender and receiver, you can register your +adapter factory to be able to use it via a DSN in the Symfony application. Create your adapter Factory ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -196,40 +180,26 @@ Create your adapter Factory You need to give FrameworkBundle the opportunity to create your adapter from a DSN. You will need an adapter factory:: - use Symfony\Component\Messenger\Adapter\Factory\AdapterInterface; use Symfony\Component\Messenger\Adapter\Factory\AdapterFactoryInterface; - - class YourAdapterFactory implements AdapterFactoryInterface - { - public function create(string $dsn): AdapterInterface - { - return new YourAdapter(/* ... */); - } - - public function supports(string $dsn): bool - { - return 0 === strpos($dsn, 'my-adapter://'); - } - } - -The ``YourAdaper`` class need to implement the ``AdapterInterface``. It -will like the following example:: - - use Symfony\Component\Messenger\Adapter\Factory\AdapterInterface; use Symfony\Component\Messenger\Transport\ReceiverInterface; use Symfony\Component\Messenger\Transport\SenderInterface; - class YourAdapter implements AdapterInterface + class YourAdapterFactory implements AdapterFactoryInterface { - public function receiver(): ReceiverInterface + public function createReceiver(string $dsn, array $options): ReceiverInterface { return new YourReceiver(/* ... */); } - public function sender(): SenderInterface + public function createSender(string $dsn, array $options): SenderInterface { return new YourSender(/* ... */); } + + public function supports(string $dsn, array $options): bool + { + return 0 === strpos($dsn, 'my-adapter://'); + } } Register your factory @@ -237,7 +207,7 @@ Register your factory .. code-block:: xml - + @@ -254,10 +224,10 @@ named adapter using your own DSN: adapters: yours: 'my-adapter://...' -This will give you access to the following services: +In addition of being able to route your messages to the ``yours`` sender, this +will give you access to the following services: -1. ``messenger.yours_adapter``: the instance of your adapter. -2. ``messenger.yours_receiver`` and ``messenger.yours_sender``, the - receiver and sender created by the adapter. +1. ``messenger.sender.hours``: the sender. +2. ``messenger.receiver.hours``: the receiver. .. _`enqueue's adapter`: https://github.com/sroze/enqueue-bridge From c5306b8f19394dae0af5d340faec5dfc7b00852e Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Sun, 15 Apr 2018 22:13:59 +0100 Subject: [PATCH 14/14] Minor wording change --- messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index f6d271eed62..0dbd7c15e9f 100644 --- a/messenger.rst +++ b/messenger.rst @@ -135,7 +135,7 @@ You can route all classes of message to a sender using an asterisk instead of a 'My\Message\MessageAboutDoingOperationalWork': another_adapter '*': amqp -A class of message can also be routed to a multiple senders by specifying a list: +A class of message can also be routed to multiple senders by specifying a list: .. code-block:: yaml