Symfony Messenger moves work such as email delivery, report generation, and webhook calls from the request path into handlers that a worker can run later. A queue setup gives Messenger a transport, routes a message class to that transport, and leaves the worker responsible for processing the message outside the original request.
Doctrine Messenger uses the Doctrine database connection many Symfony applications already have, so it avoids a separate Redis or RabbitMQ broker for a local smoke test. The same async transport name can later point at another supported DSN when the application moves to a dedicated broker.
A suitable starting project already has symfony/messenger, a message class, and a handler. The sample message class is App\Message\GenerateReport, the transport is async, and the proof is a queue count that drops from one to zero after messenger:consume handles the message.
$ composer require symfony/doctrine-messenger
The doctrine://default DSN uses the active Doctrine connection. Use the matching bridge package instead when the queue backend is Redis, AMQP, Beanstalkd, or another supported transport.
# .env.local MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=false
Configure DATABASE_URL first when doctrine://default has no database connection to use.
Related: How to configure a Doctrine database in Symfony
framework:
messenger:
failure_transport: failed
transports:
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
retry_strategy:
max_retries: 3
multiplier: 2
failed: 'doctrine://default?queue_name=failed'
framework:
messenger:
routing:
App\Message\GenerateReport: async
Messages without a matching routing rule are handled synchronously when they are dispatched.
Related: How to handle a Symfony Messenger message
$ php bin/console cache:clear // Clearing the cache for the dev environment with debug true [OK] Cache for the "dev" environment (debug=true) was successfully cleared.
Related: How to clear Symfony cache
$ php bin/console debug:config framework messenger
Current configuration for "framework.messenger"
===============================================
failure_transport: failed
transports:
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
##### snipped #####
routing:
App\Message\GenerateReport:
- async
##### snipped #####
$ php bin/console debug:messenger
Messenger
=========
messenger.bus.default
---------------------
The following messages can be dispatched:
------------------------------------------------------------------------
App\Message\GenerateReport
handled by App\MessageHandler\GenerateReportHandler
##### snipped #####
$ php bin/console messenger:setup-transports [OK] The "async" transport was set up successfully. [OK] The "failed" transport was set up successfully.
With auto_setup=false, the table is not created implicitly when the first message is sent. Run setup or a database migration during deployment before workers start.
$ php bin/console app:queue-report quarterly Queued report "quarterly".
The sample command dispatches App\Message\GenerateReport through MessageBusInterface. Use the controller, command, or service that dispatches the message in your application.
Related: How to create a Symfony console command
Related: How to handle a Symfony Messenger message
$ php bin/console messenger:stats async ----------- ------- Transport Count ----------- ------- async 1 ----------- -------
$ php bin/console messenger:consume async --limit=1 -vv [OK] Consuming messages from transport "async". INFO [messenger] Received message App\Message\GenerateReport INFO [messenger] Message App\Message\GenerateReport handled by App\MessageHandler\GenerateReportHandler::__invoke INFO [messenger] App\Message\GenerateReport was handled successfully (acknowledging to transport). INFO [messenger] Worker stopped due to maximum count of 1 messages processed
Remove --limit=1 for a long-running worker. Use a process manager such as systemd or Supervisor for production workers so they restart after deploys or failures.
$ php bin/console messenger:stats async ----------- ------- Transport Count ----------- ------- async 0 ----------- -------
$ cat var/report.log handled quarterly
For a real handler, verify the domain effect the handler owns, such as a sent email record, generated file, API call, or updated database row.