-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Symfony Messenger component documentation #9437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
585491d
b40bc71
b26de80
88ba8fe
5c828e4
31a56ee
2493c90
bcfae23
25c0b6e
a15752b
fb88abc
509e149
3fb973c
32403ea
c5306b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
.. index:: | ||
single: Messenger | ||
single: Components; Messenger | ||
|
||
The Messenger Component | ||
======================= | ||
|
||
The Messenger component helps applications send and receive messages to/from other applications or via message queues. | ||
|
||
Installation | ||
------------ | ||
|
||
.. code-block:: terminal | ||
|
||
$ composer require symfony/messenger | ||
|
||
Alternatively, you can clone the `<https://github.com/symfony/messenger>`_ repository. | ||
|
||
.. include:: /components/require_autoload.rst.inc | ||
|
||
Concepts | ||
-------- | ||
|
||
.. image:: /_images/components/messenger/overview.png | ||
|
||
**Sender**: | ||
Responsible for serializing and sending the message to _something_. This | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. - 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Reasons:
|
||
|
||
**Receiver**: | ||
Responsible for deserializing and forwarding the messages to handler(s). This | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. - 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about:-
The part about PHP callable is confusing, explained better in the Handlers section, below and I think it should be omitted here. |
||
|
||
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. | ||
|
||
When using the message bus with Symfony's FrameworkBundle, the following middlewares | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
are configured for you: | ||
|
||
#. :code:`LoggingMiddleware` (logs the processing of your messages) | ||
#. :code:`SendMessageMiddleware` (enables asynchronous processing) | ||
#. :code:`HandleMessageMiddleware` (calls the registered handle) | ||
|
||
Example:: | ||
|
||
use App\Message\MyMessage; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. - use Symfony\Component\Messenger\MessageBus;
- use Symfony\Component\Messenger\HandlerLocator;
- use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;
-
- use App\Message\MyMessage;
+ use App\Message\MyMessage;
+ use Symfony\Component\Messenger\MessageBus;
+ use Symfony\Component\Messenger\HandlerLocator;
+ use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure how it even ended like that 😂 Changed! |
||
use Symfony\Component\Messenger\MessageBus; | ||
use Symfony\Component\Messenger\HandlerLocator; | ||
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; | ||
|
||
$bus = new MessageBus([ | ||
new HandleMessageMiddleware(new HandlerLocator([ | ||
MyMessage::class => $handler, | ||
])), | ||
]); | ||
|
||
$result = $bus->handle(new MyMessage(/* ... */)); | ||
|
||
.. note: | ||
|
||
Every middleware need to implement the :code:`MiddlewareInterface` interface. | ||
|
||
Handlers | ||
-------- | ||
|
||
Once dispatched to the bus, messages will be handled by a "message handler". A | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "dispatched" is confusing here: according to the Bus section, the bus is responsible for dispatching messages; and the method used to get a message on to the bus is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it's |
||
message handler is a PHP callable (i.e. a function or an instance of a class) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we could add a link to the official doc here:
|
||
that will do the required processing for your message. It _might_ return a | ||
result:: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What happens if it does or does not return a result? Does the following example code illustrate something about this statement? |
||
|
||
namespace App\MessageHandler; | ||
|
||
use App\Message\MyMessage; | ||
|
||
class MyMessageHandler | ||
{ | ||
public function __invoke(MyMessage $message) | ||
{ | ||
// Message processing... | ||
} | ||
} | ||
|
||
Adapters | ||
-------- | ||
|
||
The communication with queuing system or third parties is delegated to | ||
libraries for now. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It isn't at all clear what this means or what are the implications for someone who intends to communicate with a queuing system or third party. Also system -> systems. And drop "for now" unless you want to say something about what will happen in the future. |
||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what Symfony's policy is on this but I refrain to use elision in docs and would write "Let us say" |
||
message bus and handled by a handler. Now, you also want to send this message as | ||
an email. | ||
|
||
First, create your sender:: | ||
|
||
namespace App\MessageSender; | ||
|
||
use App\Message\ImportantAction; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we use ordered use statements in the docs? if yes, this must be above the |
||
use Symfony\Component\Message\SenderInterface; | ||
|
||
class ImportantActionToEmailSender implements SenderInterface | ||
{ | ||
private $toEmail; | ||
private $mailer; | ||
|
||
public function __construct(\Swift_Mailer $mailer, string $toEmail) | ||
{ | ||
$this->mailer = $mailer; | ||
$this->toEmail = $toEmail; | ||
} | ||
|
||
public function send($message) | ||
{ | ||
if (!$message instanceof ImportantAction) { | ||
throw new \InvalidArgumentException(sprintf('Producer only supports "%s" messages.', ImportantAction::class)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would linebreak this, but again not sure what the norm is in Symfony. I know you guys don't like linebreaks, but maybe it's different for docs? Since you might want to avoid the wrapping |
||
} | ||
|
||
$this->mailer->send( | ||
(new \Swift_Message('Important action made')) | ||
->setTo($this->toEmail) | ||
->setBody( | ||
'<h1>Important action</h1><p>Made by '.$message->getUsername().'</p>', | ||
'text/html' | ||
) | ||
); | ||
} | ||
} | ||
|
||
Your own receiver | ||
~~~~~~~~~~~~~~~~~ | ||
|
||
A receiver is responsible for receiving messages from a source and dispatching | ||
them to the application. | ||
|
||
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 to write your custom CSV receiver and Symfony will do the rest. | ||
|
||
First, create your receiver:: | ||
|
||
namespace App\MessageReceiver; | ||
|
||
use App\Message\NewOrder; | ||
use Symfony\Component\Message\ReceiverInterface; | ||
use Symfony\Component\Serializer\SerializerInterface; | ||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's compatible |
||
{ | ||
$ordersFromCsv = $this->serializer->deserialize(file_get_contents($this->filePath), 'csv'); | ||
|
||
foreach ($ordersFromCsv as $orderFromCsv) { | ||
yield new NewOrder($orderFromCsv['id'], $orderFromCsv['account_id'], $orderFromCsv['amount']); | ||
} | ||
} | ||
} | ||
|
||
Same bus received and sender | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Capitalize. |
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
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. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ Topics | |
frontend | ||
http_cache | ||
logging | ||
messenger | ||
performance | ||
profiler | ||
routing | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.