Symfony Messenger does not consume messages until stop/consume - symfony

On Symfony 5.1, I created two very basic message classes.
The first updates the database and then it calls the other to send a mail via SwiftMailer.
Both of them arrive to the consumer command as they are logged by the bin/console messenger:consume async -vv command, but while the class that updates the database is handled, the other remains suspended.
The fact is that if I stop and restart the consume command, this message becomes handled by the consumer and so the mail is sent.
I have tried with Redis and RabbitMQ transports, with the very same results.
This is the messenger configuration:
framework:
messenger:
transports:
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
'App\Message\FollowRequestMessage': async
'App\Message\EmailMessage': async
This is the MESSENGER_TRANSPORT_DSN in .env:
MESSENGER_TRANSPORT_DSN=amqp://guest:guest#127.0.0.1:5672/%2f/messages
UPDATE
I defined another transport to handle the FollowRequest and Email messages separately, by duplicating the "async" transport config in the yaml messenger configuration.
This has fixed the issue, but I don't know if it the right way to handle the case.
So, it is correct to create a different transport for each kind of message? Or is it a workaround because of an error in something else?

Related

How can i config properly RabbitMQ, messenger and amqp on symfony 5.2 without use docker_compose.yml?

I want to submit a form with async method using messenger component and rabbitmq. After submitting this form, an email is sent to me. But when i implement rabbitmq it does not work. I have surely a problem with configuration. Maybe i have to add something in services.yml ? I dont want to use docker.
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async :
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
auto_setup: false
use_notify: true
check_delayed_interval: 60000
# failed: 'doctrine://default?queue_name=failed'
# sync: 'sync://'
routing:
# Route your messages to the transports
'App\Message\MailNotification': async
.env file with amqp because i want to use rabbitmq
MESSENGER_TRANSPORT_DSN=amqp://guest:guest#localhost:5672/%2f/messages
Output give me this result when i submit form
Could not connect to the AMQP server. Please verify the provided DSN. ({"host":"localhost","port":5672,"vhost":"/","auto_setup":false,"use_notify":true,"check_delayed_interval":60000,"login":"guest","password":"********"}).
In addition, Rabbitmq UI appears on webprofiler DOWN, it does not turn to UP. I can't figure out. Help me please.

How to send mail using different queues in new Symfony Mail component

I want to configure Symfony Mail component to send different types of emails using different queue priorities.
How can I do it?
So far I configured the queues:
framework:
messenger:
transports:
async_low: '%env(MESSENGER_TRANSPORT_DSN_LOW)%'
async_high: '%env(MESSENGER_TRANSPORT_DSN_HIGH)%'
routing:
'Symfony\Component\Mailer\Messenger\SendEmailMessage': async_high
Then I started the queue with async_high being processed first:
bin/console messenger:consume async_high async_low
Now I would like to send one type of email form higher priority queue than the other:
$this->mailer->send($newsletterEmail); // 10.000 times
$this->mailer->send($resetPasswordEmail); // Triggered after newsletter
Without priorities the password reset would be sent after an hour (after all the newsletters get processed).
How can I send the newsletters using the lower priority queue?

Switch sendmail Parameters in symfony/mailer when using symfony/messenger

The local Development Machine uses msmtp for sending Mails, which works fine. BUT it does not accept /usr/sbin/sendmail -bs, it only works with /usr/sbin/sendmail -t. I can see that I could change this when using other parameters for the SendmailTransport constructor, but actually I never create an Instance of SendmailTransport - the consumer of the Messenger does this all alone.
How can I change the Sendmail Parameters when using async Mails via Messenger?
MessageHandler receives a TransportInterface as a dependency in order to send the messages, so indeed you can change it via configuration. Since once messenger is installed all messages will be sent with it, you can just override the default transport directly and it will be injected into the Handler:
# config/services_dev.yaml
services:
Symfony\Component\Mailer\Transport\TransportInterface:
class: Symfony\Component\Mailer\Transport\SendmailTransport
arguments:
- '/usr/sbin/sendmail -t'

Symfony 3.4 and Swiftmailer - is it possible to send emails immediately/without spool?

I want emails sent by my web app to be sent immediately. However, no matter what I try to do, emails keep getting spooled by the system. My app/config/config.yml:
# Swiftmailer Configuration
swiftmailer:
transport: '%mailer_transport%'
host: '%mailer_host%'
When I add spool: { type: memory } to the config, the spool isn't sent upon kernel termination as it says in the docs. Is there a way to force immediate delivery?
If you don't specify something else in the config, mails are sent immediatly and not spooled.
You can check it out on the guide
The default behavior of the Symfony mailer is to send the email messages immediately. You may, however, want to avoid the performance hit of the communication to the email server, which could cause the user to wait for the next page to load while the email is sending.
Simply omit the spool part in the configuration and it should be fine.
If the email does not be sent out, maybe, you have errors in the email server or somewhere else not related directly with Swift mailer.
Had to clear the cache.
$ bin/console cache:clear --env=prod

Consumer Error Handling in Symfony Messenger / RabbitMQ

I'm using the new Symfony Messenger Component 4.1 and RabbitMQ 3.6.10-1 to queue and asynchronously send email and SMS notifications from my Symfony 4.1 web application. My Messenger configuration (messenger.yaml) looks like this:
framework:
messenger:
transports:
amqp: '%env(MESSENGER_TRANSPORT_DSN_NOTIFICATIONS)%'
routing:
'App\NotificationBundle\Entity\NotificationQueueEntry': amqp
When a new notification is to be sent, I queue it like this:
use Symfony\Component\Messenger\MessageBusInterface;
// ...
$notificationQueueEntry = new NotificationQueueEntry();
// [Set notification details such as recipients, subject, and message]
$this->messageBus->dispatch($notificationQueueEntry);
Then I start the consumer like this on the command line:
$ bin/console messenger:consume-messages
I have implemented a SendNotificationHandler service where the actual delivery happens. The service configuration:
App\NotificationBundle\MessageHandler\SendNotificationHandler:
arguments:
- '#App\NotificationBundle\Service\NotificationQueueService'
tags: [ messenger.message_handler ]
And the class:
class SendNotificationHandler
{
public function __invoke(NotificationQueueEntry $entry): void
{
$this->notificationQueueService->sendNotification($entry);
}
}
Until this point, everything works smoothly and the notifications get delivered.
Now my question: It may happen that an email or SMS cannot be delivered due to a (temporary) network failure. In such a case, I would like my system to retry the delivery after a specified amount of time, up to a specified maximum number of retries. What is the way to go to achieve this?
I have read about Dead Letter Exchanges, however, I could not find any documentation or example on how to integrate this with the Symfony Messenger Component.
What you need to do is tell RabbitMQ, that the message is rejected instead of acknowledged. By default the messenger will take care of this inside the AmqpReceiver. As you can see there, if you throw an exception that implements the RejectMessageExceptionInterface inside your handler, the message will automatically be rejected for you.
You could also "simulate" this behaviour with custom middleware. I created something like it, in a small demo application. The mechanism consists of a middleware that wraps the (serialized) original message inside a new RetryMessage and sends it via a custom message bus to a different queue, used as a dead letter exchange. The handler for that message will then unpack the RetryMessage (getting the original message and deserializing it) and transmit it over the default bus:
See:
RetryMessage
RetryMiddleware
messenger.yaml
RetryMessageHandler
This is a basic setup which rejects the message and allows you to consume it again instantly(!). You probably want to add additional information such as headers for timestamps when delaying the consumption to improve on this. For this you should look at writing your own receiver, middleware and/or handler.

Resources