异常处理
RabbitMQ Java 客户端的许多操作可能会抛出受检异常。例如,在许多情况下可能会抛出 IOException
实例。RabbitTemplate
、SimpleMessageListenerContainer
和其他 Spring AMQP 组件会捕获这些异常,并将它们转换为 AmqpException
层次结构中的一个异常。这些异常定义在 'org.springframework.amqp' 包中,AmqpException
是层次结构的基类。
当监听器抛出异常时,它会被包装在 ListenerExecutionFailedException
中。通常,消息会被拒绝并由代理重新排队。将 defaultRequeueRejected
设置为 false
会导致消息被丢弃(或路由到死信交换)。如 消息监听器和异步情况 中所述,监听器可以抛出 AmqpRejectAndDontRequeueException
(或 ImmediateRequeueAmqpException
)来有条件地控制此行为。
但是,有一类错误是监听器无法控制其行为的。当遇到无法转换的消息时(例如,无效的 content_encoding
标头),在消息到达用户代码之前会抛出一些异常。当 defaultRequeueRejected
设置为 true
(默认值)(或抛出 ImmediateRequeueAmqpException
)时,此类消息将被反复重新传递。在 1.3.2 版本之前,用户需要编写自定义 ErrorHandler
,如 异常处理 中所述,以避免这种情况。
从 1.3.2 版本开始,默认的 ErrorHandler
现在是 ConditionalRejectingErrorHandler
,它会拒绝(并且不会重新排队)那些遇到不可恢复错误而失败的消息。具体来说,它会拒绝以下错误导致失败的消息
-
o.s.amqp…MessageConversionException
:当使用MessageConverter
转换传入的消息负载时可能会抛出此异常。 -
o.s.messaging…MessageConversionException
:如果在映射到@RabbitListener
方法时需要额外的转换,则转换服务可能会抛出此异常。 -
o.s.messaging…MethodArgumentNotValidException
:如果在监听器中使用验证(例如,@Valid
)并且验证失败,则可能会抛出此异常。 -
o.s.messaging…MethodArgumentTypeMismatchException
:如果传入的消息被转换为不适合目标方法的类型,则可能会抛出此异常。例如,参数声明为Message<Foo>
,但接收到的却是Message<Bar>
。 -
java.lang.NoSuchMethodException
:在 1.6.3 版本中添加。 -
java.lang.ClassCastException
:在 1.6.3 版本中添加。
您可以使用 FatalExceptionStrategy
配置此错误处理器的实例,以便用户可以提供自己的条件消息拒绝规则,例如,一个委托实现到 Spring Retry 中的 BinaryExceptionClassifier
(消息监听器和异步情况)。此外,ListenerExecutionFailedException
现在有一个 failedMessage
属性,您可以在决策中使用它。如果 FatalExceptionStrategy.isFatal()
方法返回 true
,则错误处理器会抛出一个 AmqpRejectAndDontRequeueException
。默认的 FatalExceptionStrategy
在确定异常为致命异常时会记录一条警告消息。
从 1.6.3 版本开始,向致命列表添加用户异常的一种便捷方法是子类化 ConditionalRejectingErrorHandler.DefaultExceptionStrategy
并覆盖 isUserCauseFatal(Throwable cause)
方法,对于致命异常返回 true
。
处理 DLQ 消息的一种常见模式是在这些消息上设置 time-to-live
以及额外的 DLQ 配置,以便这些消息过期并被路由回主队列以重试。这种技术的问题在于,导致致命异常的消息会无限循环。从 2.1 版本开始,ConditionalRejectingErrorHandler
会检测导致抛出致命异常的消息上的 x-death
标头。该消息将被记录并丢弃。您可以通过将 ConditionalRejectingErrorHandler
上的 discardFatalsWithXDeath
属性设置为 false
来恢复到之前的行为。
从 2.1.9 版本开始,即使容器确认模式为 MANUAL,具有这些致命异常的消息也会被默认拒绝,并且不会重新排队。这些异常通常在监听器被调用之前发生,因此监听器没有机会确认或否定消息,因此它会以未确认状态保留在队列中。要恢复到之前的行为,请将 ConditionalRejectingErrorHandler 上的 rejectManual 属性设置为 false 。
|