Expand Up @@ -5,7 +5,7 @@ How to Use the Messenger ======================== Symfony's Messenger provide a message bus and some routing capabilities to send messages within your application and throughadapters such as message queues. messages within your application and throughtransports such as message queues. Before using it, read the :doc:`Messenger component docs </components/messenger>` to get familiar with its concepts. Expand Down Expand Up @@ -44,7 +44,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; Expand All @@ -70,19 +70,19 @@ Once you've created your handler, you need to register it: If the message cannot be guessed from the handler's type-hint, use the ``handles`` attribute on the tag. Adapters -------- Transports ---------- The communication with queuing system or third parties is delegated to libraries for now. The built-in AMQPadapter allows you to communicate with libraries for now. The built-in AMQPtransport 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'sadapter `_ If you need more message brokers, you should have a look to `Enqueue'stransport `_ which supports things like Kafka, Amazon SQS or Google Pub/Sub. An adapter is registered using a "DSN", which is a string that represents theA transport is registered using a "DSN", which is a string that represents theconnection credentials and configuration. By default, when you've installed the messenger component, the following configuration should have been created: Expand All @@ -91,7 +91,7 @@ the messenger component, the following configuration should have been created: # config/packages/messenger.yaml framework: messenger: adapters :transports : amqp: "%env(MESSENGER_DSN)%" .. code-block:: bash Expand All @@ -107,11 +107,20 @@ configure the following services for you: 1. A ``messenger.sender.amqp`` sender to be used when routing messages. 2. A ``messenger.receiver.amqp`` receiver to be used when consuming messages. .. note:: In order to use Symfony's built-in AMQP transport, you will need the Serializer Component. Ensure that it is installed with: .. code-block:: terminal $ composer require symfony/serializer-pack Routing ------- Instead of calling a handler, you have the option to route your message(s) to a sender. Part ofan adapter , it is responsible for sending your message somewhere. sender. Part ofa transport , it is responsible for sending your message somewhere. You can configure which message is routed to which sender with the following configuration: Expand All @@ -120,7 +129,7 @@ configuration: framework: messenger: routing: 'My\Message\Message': amqp # The name of the definedadapter 'My\Message\Message': amqp # The name of the definedtransport Such configuration would only route the ``My\Message\Message`` message to be asynchronous, the rest of the messages would still be directly handled. Expand All @@ -132,7 +141,7 @@ You can route all classes of message to a sender using an asterisk instead of a framework: messenger: routing: 'My\Message\MessageAboutDoingOperationalWork':another_adapter 'My\Message\MessageAboutDoingOperationalWork':another_transport '*': amqp A class of message can also be routed to multiple senders by specifying a list: Expand Down Expand Up @@ -166,39 +175,153 @@ like this: $ 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. your ``transports`` configuration or it can be your own receiver. Multiple buses -------------- If you are interested into architectures like CQRS, you might want to have multiple buses within your application. You can create multiple buses (in this example, a command and an event bus) like this: .. code-block:: yaml framework: messenger: # The bus that is going to be injected when injecting MessageBusInterface: default_bus: commands # Create buses buses: messenger.bus.commands: ~ messenger.bus.events: ~ Your own Adapters ----------------- This will generate the ``messenger.bus.commands`` and ``messenger.bus.events`` services that you can inject in your services. Once you have written your adapter's sender andreceiver, you can register your adapter factory to be able to use it via a DSN in the Symfony application. Type-hints andauto-wiring ~~~~~~~~~~~~~~~~~~~~~~~~~~ Create your adapter Factory ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Auto-wiring is a great feature that allows you to reduce the amount of configuration required for your service container to be created. When using multiple buses, by default, the auto-wiring will not work as it won't know why bus to inject in your own services. You need to give FrameworkBundle the opportunity to create your adapter from a DSN. You will need an adapter factory:: In order to clarify this, you can use the DependencyInjection's binding capabilities to clarify which bus will be injected based on the argument's name: .. code-block:: yaml # config/services.yaml services: _defaults: # ... bind: $commandBus: '@messenger.bus.commands' $eventBus: '@messenger.bus.events' Middleware ---------- What happens when you dispatch a message to a message bus(es) depends on its collection of middleware (and their order). By default, the middleware configured for each bus looks like this: 1. ``logging`` middleware. Responsible of logging the beginning and the end of the message within the bus. 2. _Your own collection of middleware_ 3. ``route_messages`` middleware. Will route the messages your configured to their corresponding sender and stop the middleware chain. 4. ``call_message_handler`` middleware. Will call the message handler(s) for the given message. Adding your own middleware ~~~~~~~~~~~~~~~~~~~~~~~~~~ As described in the component documentation, you can add your own middleware within the buses to add some extra capabilities like this: .. code-block:: yaml use Symfony\Component\Messenger\Adapter\Factory\AdapterFactoryInterface; framework: messenger: buses: messenger.bus.default: middleware: - 'App\Middleware\MyMiddleware' - 'App\Middleware\AnotherMiddleware' Note that if the service is abstract, then a different instance of service will be created per bus. Disabling default middleware ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you don't want the default collection of middleware to be present on your bus, you can disable them like this: .. code-block:: yaml framework: messenger: buses: messenger.bus.default: default_middleware: false Your own Transport ------------------ Once you have written your transport's sender and receiver, you can register your transport factory to be able to use it via a DSN in the Symfony application. Create your Transport Factory ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You need to give FrameworkBundle the opportunity to create your transport from a DSN. You will need an transport factory:: use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Messenger\Transport\TransportInterface; use Symfony\Component\Messenger\Transport\ReceiverInterface; use Symfony\Component\Messenger\Transport\SenderInterface; classYourAdapterFactory implementsAdapterFactoryInterface classYourTransportFactory implementsTransportFactoryInterface { public functioncreateReceiver (string $dsn, array $options):ReceiverInterface public functioncreateTransport (string $dsn, array $options):TransportInterface { return newYourReceiver (/* ... */); return newYourTransport (/* ... */); } public functioncreateSender (string $dsn, array $options):SenderInterface public functionsupports (string $dsn, array $options):bool { returnnew YourSender(/* ... */ ); return0 === strpos($dsn, 'my-transport://' ); } } public function supports(string $dsn, array $options): bool The transport object is needs to implements the ``TransportInterface`` (which simply combine the ``SenderInterface`` and ``ReceiverInterface``). It will look like this:: class YourTransport implements TransportInterface { public function send($message) : void { // ... } public function receive(callable $handler) : void { // ... } public function stop() : void { return 0 === strpos($dsn, 'my-adapter://'); // ... } } Expand All @@ -207,27 +330,27 @@ Register your factory .. code-block:: xml <service id="Your\Adapter\YourAdapterFactory "> <tag name="messenger.adapter_factory " /> <service id="Your\Transport\YourTransportFactory "> <tag name="messenger.transport_factory " /> </service> Use youradapter ~~~~~~~~~~~~~~~~ Use yourtransport ~~~~~~~~~~~~~~~~~~ Within the ``framework.messenger.adapters .*`` configuration, create your namedadapter using your own DSN: Within the ``framework.messenger.transports .*`` configuration, create your namedtransport using your own DSN: .. code-block:: yaml framework: messenger: adapters : yours: 'my-adapter ://...' transports : yours: 'my-transport ://...' In addition of being able to route your messages to the ``yours`` sender, this will give you access to the following services: #. ``messenger.sender.yours``: the sender. #. ``messenger.receiver.yours``: the receiver. .. _`enqueue'sadapter `: https://github.com/sroze/ enqueue-bridge .. _`enqueue'stransport `: https://github.com/enqueue/messenger-adapter