-
-
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 1 commit
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
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
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:: | ||
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. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
||
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. Please add something like 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. Added 👍 |
||
|
@@ -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. | ||
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. Instead of two sentences, one before the config example and one after, how about just one:- "The following configuration shows how you can route a class of messages to a sender, leaving other classes of messages to be passed to their respective handlers." 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. Also, routing to
It should be 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's less clear with a single example IMHO. I've updated the sender configuration 👍 |
||
|
||
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: | ||
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. - 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 | ||
|
||
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,81 +163,51 @@ 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 </components/messenger>`. | ||
|
||
To register your middleware, use the ``messenger.middleware`` tag as in the | ||
following example: | ||
|
||
.. code-block:: xml | ||
|
||
<service id="Your\Own\Middleware"> | ||
<tag name="messenger.middleware" /> | ||
</service> | ||
|
||
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 | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
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 | ||
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.
|
||
~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
.. code-block:: xml | ||
|
||
<service id="Your\Adapter\Factory"> | ||
<service id="Your\Adapter\YourAdapterFactory"> | ||
<tag name="messenger.adapter_factory" /> | ||
</service> | ||
|
||
|
@@ -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 |
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.
a messager handler, broker or a third-party API
.Reasons:
message handler
which is likely to be more commonly known will make it more accessible