Exception Handling
许多带有 RabbitMQ Java 客户端的操作可能会抛出已检查异常。例如,有很多情况都可能会抛出 IOException
实例。RabbitTemplate
、SimpleMessageListenerContainer
和其他 Spring AMQP 组件捕获这些异常并将其转换为 AmqpException
层次结构中的异常之一。这些在“org.springframework.amqp”程序包中定义,AmqpException
是该层次结构的基类。
当监听器抛出异常时,该异常将被封装在 ListenerExecutionFailedException
中。通常,消息会被代理拒绝并重新排队。将 defaultRequeueRejected
设置为 false
会导致消息被丢弃(或路由到死信交换)。如 Message Listeners and the Asynchronous Case 中所讨论的,监听器可以抛出 AmqpRejectAndDontRequeueException
(或 ImmediateRequeueAmqpException
)来有条件地控制此行为。
但是,有一类错误是侦听器无法控制行为的。当遇到不可转换的消息时(例如,无效的 content_encoding
标头),一些异常会在消息到达用户代码之前抛出。如果将 defaultRequeueRejected
设置为 true
(默认) (或抛出 ImmediateRequeueAmqpException
),此类消息将被反复重新传递。在 1.3.2 版本之前,用户需要编写一个自定义 ErrorHandler
,如 Exception Handling 中所述,以避免这种情况。
从版本 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 (Message Listeners and the Asynchronous Case) 的 BinaryExceptionClassifier
委托实现。此外,ListenerExecutionFailedException
现在有一个 failedMessage
属性,您可以在决策中使用它。如果 FatalExceptionStrategy.isFatal()
方法返回 true
,错误处理程序将抛出 AmqpRejectAndDontRequeueException
。默认 FatalExceptionStrategy
会在确定异常为致命时记录警告消息。
自版本 1.6.3 以来,向 fatal 列表添加用户异常的便捷方式是,对 ConditionalRejectingErrorHandler.DefaultExceptionStrategy
进行子类化,并重写 isUserCauseFatal(Throwable cause)
方法,使其对 fatal 异常返回 true
。
处理 DLQ 消息的常用模式是设置这些消息的 存活时间
以及额外的 DLQ 配置,这些消息将过期并路由回主队列进行重试。此技术的问题是导致 fatal 异常的消息会无限循环。从版本 2.1 开始,ConditionalRejectingErrorHandler
检测导致抛出 fatal 异常的消息上的 x-death
头。消息被记录并丢弃。你可以通过在 ConditionalRejectingErrorHandler
上将 discardFatalsWithXDeath
属性设置为 false
来还原为以前的行为。
从版本 2.1.9 开始,即使容器确认模式是 MANUAL,这些致命异常的消息也会被拒绝并且默认情况下不会重新排队。这些异常通常在调用侦听器之前发生,因此侦听器没有机会确认或拒绝消息,因此它以未确认状态保留在队列中。要恢复到之前的行为,请在 ConditionalRejectingErrorHandler
上设置 rejectManual
属性为 false
。