[英]Symfony Messenger: is it possible to not throw the exception on last retry?
We're using Symfony Messenger, and have these transports:我们正在使用 Symfony Messenger,并有这些传输:
framework:
messenger:
failure_transport: failed
transports:
failed:
dsn: 'doctrine://default?queue_name=failed'
options:
table_name: 'MessengerMessages'
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
retry_strategy:
max_retries: 3
delay: 5000
multiplier: 2
max_delay: 0
asyncLowPriority:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%_low_priority'
retry_strategy:
max_retries: 5
delay: 3600000
multiplier: 2
max_delay: 0
sync: 'sync://'
When we send a message to the async
queue, and the last retry fails with an exception, the exception is logged to the MessengerMessages
table, and the exception bubbles up (goes to Sentry in our case).当我们向
async
队列发送消息,并且最后一次重试失败并出现异常时,异常会记录到MessengerMessages
表中,并且异常会冒泡(在我们的例子中进入Sentry )。 This is what we want.这就是我们想要的。
When we send a message to the asyncLowPriority
queue however, we would like failed messages to:然而,当我们向
asyncLowPriority
队列发送消息时,我们希望失败的消息:
failed
transportfailed
传输Basically, the exception should be dropped.基本上,应该删除异常。
Is this possible, and how?这是可能的,如何?
The reason is that we're using this queue for downloading images asynchronously, and we already log each failure in a dedicated database table in the command handler.原因是我们使用这个队列异步下载图像,并且我们已经在命令处理程序的专用数据库表中记录了每个故障。
I managed to do this with a middleware:我设法用中间件做到了这一点:
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
use Throwable;
final class BypassFailureTransportMiddleware implements MiddlewareInterface
{
public function __construct(
private string $transportName,
private int $maxRetries,
) {
}
public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
try {
return $stack->next()->handle($envelope, $stack);
} catch (HandlerFailedException $exception) {
$nestedException = $this->getNestedException($exception);
if ($nestedException === null) {
throw $exception;
}
/** @var ReceivedStamp|null $receivedStamp */
$receivedStamp = $envelope->last(ReceivedStamp::class);
if ($receivedStamp === null || $receivedStamp->getTransportName() !== $this->transportName) {
throw $exception;
}
if (!$this->isLastRetry($envelope, $nestedException)) {
throw $exception;
}
return $envelope->with(new SentToFailureTransportStamp($receivedStamp->getTransportName()));
}
}
private function getNestedException(HandlerFailedException $exception): ?Throwable
{
$nestedExceptions = $exception->getNestedExceptions();
if (count($nestedExceptions) === 1) {
return $nestedExceptions[0];
}
return null;
}
private function isLastRetry(Envelope $envelope, Throwable $nestedException): bool
{
if ($nestedException instanceof UnrecoverableMessageHandlingException) {
return true;
}
/** @var RedeliveryStamp|null $redeliveryStamp */
$redeliveryStamp = $envelope->last(RedeliveryStamp::class);
if ($redeliveryStamp === null) {
return false;
}
return $redeliveryStamp->getRetryCount() === $this->maxRetries;
}
}
It must be configured with the name of the transport and the configured max_retries
of this transport:必须使用传输名称和此传输的已配置
max_retries
进行配置:
parameters:
async_allow_failure_transport_name: 'asyncAllowFailure'
async_allow_failure_max_retries: 5
services:
command.bus.bypass_failure_transport_middleware:
class: App\Infrastructure\CommandBus\Middleware\BypassFailureTransportMiddleware
arguments:
$transportName: '%async_allow_failure_transport_name%'
$maxRetries: '%async_allow_failure_max_retries%'
framework:
messenger:
transports:
- name: '%async_allow_failure_transport_name%'
dsn: '...'
retry_strategy:
max_retries: '%async_allow_failure_max_retries%'
delay: 1000
multiplier: 2
max_delay: 0
buses:
command.bus:
middleware:
- 'command.bus.bypass_failure_transport_middleware'
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.