rabbitmq 和 spring-rabbitmq 中的 DLX - 拒绝消息的一些注意事项
DLX in rabbitmq and spring-rabbitmq - some considerations of rejecting messages
我确实阅读了这篇参考文献:https://www.rabbitmq.com/dlx.html,但是它并没有解决我的疑虑,即:
在接受消息的情况下没有问题 - spring-rabbitmq
发送确认并且一切都很好,DLX
不知道已确认的消息。
问题是万一拒绝回答,即抛出 MessageConverterException
怎么办?此消息已删除或移至 DLX
?
如果出现其他异常怎么办?例如 Exception
?是 removed/requeued/moved 到 DLX
吗?
在@Gary
回答后编辑
我认为,在回答@Gary 之后,我应该添加有关我的案例的更多详细信息以及@Gary 的回答的一些摘要。 @Gary 完全掌握了我的用例。
我不喜欢重新排队 - 从不(我害怕循环),但我不想在抛出异常时丢失消息(例如丢失与数据库的连接) - 此消息应该重新发送到DLX
。另一方面,消息的转换应该被视为致命错误 - 没有重新排队,没有重新发送到 DLX - 只是永久删除消息。 通常,取决于异常情况,要么拒绝(=重新发送到 DLX,如果已配置)要么接受,从不重新排队。
简而言之,@Gary 提出的方法。
首先:我们可以覆盖ExceptionHandler
来管理发送nack/ack,这给了我们完全的控制权。
第二个:IMO 更简单,解决方案是设置defaultRequeueRejected=false
并在转换器中抛出ImmediateAcknowledgeAmqpException
。它使 RabbitMQ
认为答案已被接受(与第一个解决方案的情况相同),而且不会调用侦听器。
**Conclusion**: Using
ImmediateAcknowledgeAmqpExceptionor
ExceptionHandler` 异常我们可以完全控制永久拒绝消息(under hood ack)并重新发送到 DLX。
RabbitMQ 对异常一无所知。
当容器捕捉到异常时,它会调用 channel.basicReject(deliveryTag, requeue)
。
如果 requeue 为 true,则消息被重新排队。
默认情况下,对于未提及的任何异常 here
o.s.amqp...MessageConversionException
o.s.messaging...MessageConversionException
o.s.messaging...MethodArgumentNotValidException
o.s.messaging...MethodArgumentTypeMismatchException
java.lang.NoSuchMethodException
java.lang.ClassCastException
requeue
设置为 true,因此消息重新排队。
对于那些例外情况,传递被认为是致命的并且消息不会重新排队,它将转到 DLX/DLQ 如果已配置。
容器有一个标志defaultRequeueRejected
,默认为真;如果将其设置为 false
;没有例外将被重新排队。
对于应用级别的异常,一般情况下,消息会重新排队。要动态拒绝(而不是重新排队)一条消息,请确保在原因链中有一个 AmqpRejectAndDontRequeueException
。这指示容器不要重新排队消息,它将转到 DLX/DLQ(如果已配置)。此行为由上面提到的 defaultRequeueRejected
标志启用。
这一切都在文档中进行了解释,正如我在给您的其他答案中所讨论的那样,您可以通过使用自定义错误处理程序来更改此行为;文档中也对此进行了解释。
不可能将某些异常发送给 DLX/DLQ 而不是其他异常; rabbit 只有一个二元选项,重新排队或不重新排队,对于后者,如果配置了 DLX/DLQ,所有这些被拒绝的消息都会转到 DLX/DLQ.
Spring AMQP 又提供了一个例外,ImmediateAcknowledgeAmqpException
。如果您的侦听器抛出此异常,则消息将被确认,就好像它已成功处理一样 (channel.basicAck()
)。这是容器提供的唯一技术,可以丢弃错误消息而不将其发送到 DLX/DLQ.
当然,您的应用程序可以自行删除此类消息。
如果您想 DLX/DLQ 所有业务异常但丢弃转换异常,请抛出 AmqpRejectAndDontRequeueException
(或将 defaultRequeueRejected
设置为 false),然后从您的转换器中抛出 ImmediateAcknowledgeAmqpException
.
我确实阅读了这篇参考文献:https://www.rabbitmq.com/dlx.html,但是它并没有解决我的疑虑,即:
在接受消息的情况下没有问题 - spring-rabbitmq
发送确认并且一切都很好,DLX
不知道已确认的消息。
问题是万一拒绝回答,即抛出 MessageConverterException
怎么办?此消息已删除或移至 DLX
?
如果出现其他异常怎么办?例如 Exception
?是 removed/requeued/moved 到 DLX
吗?
在@Gary
回答后编辑
我认为,在回答@Gary 之后,我应该添加有关我的案例的更多详细信息以及@Gary 的回答的一些摘要。 @Gary 完全掌握了我的用例。
我不喜欢重新排队 - 从不(我害怕循环),但我不想在抛出异常时丢失消息(例如丢失与数据库的连接) - 此消息应该重新发送到DLX
。另一方面,消息的转换应该被视为致命错误 - 没有重新排队,没有重新发送到 DLX - 只是永久删除消息。 通常,取决于异常情况,要么拒绝(=重新发送到 DLX,如果已配置)要么接受,从不重新排队。
简而言之,@Gary 提出的方法。
首先:我们可以覆盖ExceptionHandler
来管理发送nack/ack,这给了我们完全的控制权。
第二个:IMO 更简单,解决方案是设置defaultRequeueRejected=false
并在转换器中抛出ImmediateAcknowledgeAmqpException
。它使 RabbitMQ
认为答案已被接受(与第一个解决方案的情况相同),而且不会调用侦听器。
**Conclusion**: Using
ImmediateAcknowledgeAmqpExceptionor
ExceptionHandler` 异常我们可以完全控制永久拒绝消息(under hood ack)并重新发送到 DLX。
RabbitMQ 对异常一无所知。
当容器捕捉到异常时,它会调用 channel.basicReject(deliveryTag, requeue)
。
如果 requeue 为 true,则消息被重新排队。
默认情况下,对于未提及的任何异常 here
o.s.amqp...MessageConversionException
o.s.messaging...MessageConversionException
o.s.messaging...MethodArgumentNotValidException
o.s.messaging...MethodArgumentTypeMismatchException
java.lang.NoSuchMethodException
java.lang.ClassCastException
requeue
设置为 true,因此消息重新排队。
对于那些例外情况,传递被认为是致命的并且消息不会重新排队,它将转到 DLX/DLQ 如果已配置。
容器有一个标志defaultRequeueRejected
,默认为真;如果将其设置为 false
;没有例外将被重新排队。
对于应用级别的异常,一般情况下,消息会重新排队。要动态拒绝(而不是重新排队)一条消息,请确保在原因链中有一个 AmqpRejectAndDontRequeueException
。这指示容器不要重新排队消息,它将转到 DLX/DLQ(如果已配置)。此行为由上面提到的 defaultRequeueRejected
标志启用。
这一切都在文档中进行了解释,正如我在给您的其他答案中所讨论的那样,您可以通过使用自定义错误处理程序来更改此行为;文档中也对此进行了解释。
不可能将某些异常发送给 DLX/DLQ 而不是其他异常; rabbit 只有一个二元选项,重新排队或不重新排队,对于后者,如果配置了 DLX/DLQ,所有这些被拒绝的消息都会转到 DLX/DLQ.
Spring AMQP 又提供了一个例外,ImmediateAcknowledgeAmqpException
。如果您的侦听器抛出此异常,则消息将被确认,就好像它已成功处理一样 (channel.basicAck()
)。这是容器提供的唯一技术,可以丢弃错误消息而不将其发送到 DLX/DLQ.
当然,您的应用程序可以自行删除此类消息。
如果您想 DLX/DLQ 所有业务异常但丢弃转换异常,请抛出 AmqpRejectAndDontRequeueException
(或将 defaultRequeueRejected
设置为 false),然后从您的转换器中抛出 ImmediateAcknowledgeAmqpException
.