异常处理
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 消息的常见模式是为这些消息设置 生存时间
以及其他 DLQ 配置,以便这些消息过期并被路由回主队列以进行重试。此技术的缺点是导致致命异常的消息会无限循环。从 2.1 版本开始,ConditionalRejectingErrorHandler
会检测导致抛出致命异常的消息上的 x-death
标头。该消息会被记录并丢弃。您可以通过将 ConditionalRejectingErrorHandler
上的 discardFatalsWithXDeath
属性设置为 false
来恢复到之前的行为。
从 2.1.9 版本开始,即使容器确认模式为 MANUAL,包含这些致命异常的消息也会被拒绝并且默认情况下**不会**重新入队。这些异常通常在监听器被调用之前发生,因此监听器没有机会确认或否定消息,导致消息停留在队列中处于未确认状态。要恢复到之前的行为,请将 ConditionalRejectingErrorHandler 上的 rejectManual 属性设置为 false 。 |