Symfony Messenger/RabbitMQ中的使用者错误处理

Symfony Messenger/RabbitMQ中的使用者错误处理,symfony,rabbitmq,dead-letter,symfony-messenger,Symfony,Rabbitmq,Dead Letter,Symfony Messenger,我正在使用新的4.1和3.6.10-1从我的Symfony 4.1 web应用程序排队并异步发送电子邮件和短信通知。我的Messenger配置(Messenger.yaml)如下所示: framework: messenger: transports: amqp: '%env(MESSENGER_TRANSPORT_DSN_NOTIFICATIONS)%' routing: 'App\Notificatio

我正在使用新的4.1和3.6.10-1从我的Symfony 4.1 web应用程序排队并异步发送电子邮件和短信通知。我的Messenger配置(
Messenger.yaml
)如下所示:

framework:
    messenger:
        transports:
            amqp: '%env(MESSENGER_TRANSPORT_DSN_NOTIFICATIONS)%'

        routing:
            'App\NotificationBundle\Entity\NotificationQueueEntry': amqp
use Symfony\Component\Messenger\MessageBusInterface;
// ...
$notificationQueueEntry = new NotificationQueueEntry();
// [Set notification details such as recipients, subject, and message]
$this->messageBus->dispatch($notificationQueueEntry);
当要发送新通知时,我将其按如下方式排队:

framework:
    messenger:
        transports:
            amqp: '%env(MESSENGER_TRANSPORT_DSN_NOTIFICATIONS)%'

        routing:
            'App\NotificationBundle\Entity\NotificationQueueEntry': amqp
use Symfony\Component\Messenger\MessageBusInterface;
// ...
$notificationQueueEntry = new NotificationQueueEntry();
// [Set notification details such as recipients, subject, and message]
$this->messageBus->dispatch($notificationQueueEntry);
然后我在命令行上启动消费者,如下所示:

$ bin/console messenger:consume-messages
我已经实现了一个实际交付发生的
SendNotificationHandler
服务。服务配置:

App\NotificationBundle\MessageHandler\SendNotificationHandler:
    arguments:
        - '@App\NotificationBundle\Service\NotificationQueueService'
    tags: [ messenger.message_handler ]
班级:

class SendNotificationHandler
{
    public function __invoke(NotificationQueueEntry $entry): void
    {
        $this->notificationQueueService->sendNotification($entry);
    }
}
在这之前,一切都会顺利进行,通知也会送达。

现在我的问题:可能会发生由于(临时)网络故障而无法发送电子邮件或短信的情况。在这种情况下,我希望我的系统在指定的时间后重试传递,最多重试指定的最大次数实现这一目标的方法是什么?


但是,我读过关于如何将其与Symfony Messenger组件集成的任何文档或示例。

您需要做的是告诉RabbitMQ消息被拒绝而不是确认。默认情况下,messenger将在内部处理此问题。如您所见,如果您在处理程序中抛出一个实现
RejectMessageExceptionInterface
的异常,消息将自动被拒绝

您还可以使用自定义中间件“模拟”这种行为。我在一个小的演示应用程序中创建了类似的东西。该机制由一个中间件组成,该中间件将(序列化的)原始消息封装在新的
RetryMessage
中,并通过自定义消息总线将其发送到另一个队列,用作死信交换。然后,该消息的处理程序将解包RetryMessage(获取原始消息并对其进行反序列化),并通过默认总线进行传输:

见:


这是一个基本的设置,它拒绝消息并允许您立即再次使用它(!)。您可能希望在延迟消费时添加额外的信息,例如时间戳的标题,以改进这一点。为此,您应该考虑编写自己的接收器、中间件和/或处理程序。

对于处理死信消息时有用的标题,您可以检查:两个问题:1。)当我拒绝消息时,RabbitMQ中会发生什么?我如何配置它将被推送到哪个队列,在哪个时间间隔内,以及它将被重新交付多少次?2.)当我抛出一个实现的异常时,
RejectMessageExceptionInterface
bin/console messenger:consume messages
命令退出,因为异常被捕获,然后立即再次抛出(如
AmqpReceiver
中所示。我如何使其保持活动状态?1)要在RabbitMQ中将exchange声明为DLX,可以使用cli工具。据我所知,路由密钥(即队列名称)将被保留,因此您在不同的交换下拥有相同的队列,但您可以通过设置一个路由密钥(如x-dead-letter-routing-key(?)来修改此设置。2) 命令退出不应该是一个问题,因为您应该经常重新启动它。您可以使用像supervisord这样的流程管理器。如果不希望出现这种行为,则必须编写自己的中间件,或者修改SendMessageMiddleware并禁用默认设置。