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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy