diff --git a/components/messenger.rst b/components/messenger.rst index 53e4687812e..a784cce16f7 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -86,11 +86,11 @@ that will do the required processing for your message:: } } -Adapters --------- +Transports +---------- -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. +In order to send and receive messages, you will have to configure a transport. An +transport will be responsible of communicating with your message broker or 3rd parties. Your own sender ~~~~~~~~~~~~~~~ @@ -190,4 +190,4 @@ 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. 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. +messages again to a transport. diff --git a/messenger.rst b/messenger.rst index 94cfb9a63b3..ed73d148ae2 100644 --- a/messenger.rst +++ b/messenger.rst @@ -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 through adapters such as message queues. +messages within your application and through transports such as message queues. Before using it, read the :doc:`Messenger component docs ` to get familiar with its concepts. @@ -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; @@ -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 AMQP adapter allows you to communicate with +libraries for now. The built-in AMQP transport 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`_ + If you need more message brokers, you should have a look to `Enqueue's transport`_ 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 +A transport 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: @@ -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 @@ -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 of an adapter, it is responsible for sending your message somewhere. +sender. Part of a transport, it is responsible for sending your message somewhere. You can configure which message is routed to which sender with the following configuration: @@ -120,7 +129,7 @@ configuration: framework: messenger: routing: - 'My\Message\Message': amqp # The name of the defined adapter + 'My\Message\Message': amqp # The name of the defined transport Such configuration would only route the ``My\Message\Message`` message to be asynchronous, the rest of the messages would still be directly handled. @@ -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: @@ -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 and receiver, you can register your -adapter factory to be able to use it via a DSN in the Symfony application. +Type-hints and auto-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; - class YourAdapterFactory implements AdapterFactoryInterface + class YourTransportFactory implements TransportFactoryInterface { - public function createReceiver(string $dsn, array $options): ReceiverInterface + public function createTransport(string $dsn, array $options): TransportInterface { - return new YourReceiver(/* ... */); + return new YourTransport(/* ... */); } - public function createSender(string $dsn, array $options): SenderInterface + public function supports(string $dsn, array $options): bool { - return new YourSender(/* ... */); + return 0 === 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://'); + // ... } } @@ -207,22 +330,22 @@ Register your factory .. code-block:: xml - - + + -Use your adapter -~~~~~~~~~~~~~~~~ +Use your transport +~~~~~~~~~~~~~~~~~~ -Within the ``framework.messenger.adapters.*`` configuration, create your -named adapter using your own DSN: +Within the ``framework.messenger.transports.*`` configuration, create your +named transport 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: @@ -230,4 +353,4 @@ will give you access to the following services: #. ``messenger.sender.yours``: the sender. #. ``messenger.receiver.yours``: the receiver. -.. _`enqueue's adapter`: https://github.com/sroze/enqueue-bridge +.. _`enqueue's transport`: https://github.com/enqueue/messenger-adapter