异常处理
RabbitMQ Java 客户端的许多操作可能会抛出受检异常(checked exception)。例如,在许多情况下可能会抛出 IOException
实例。RabbitTemplate
、SimpleMessageListenerContainer
和其他 Spring AMQP 组件会捕获这些异常,并将它们转换为 AmqpException
层次结构中的一种异常。这些异常在 'org.springframework.amqp' 包中定义,AmqpException
是该层次结构的基类。
当监听器抛出异常时,它会被包装在 ListenerExecutionFailedException
中。通常情况下,消息会被 Broker 拒绝并重新排队。将 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 版本开始,默认情况下,即使容器的确认模式(acknowledge mode)是 MANUAL,带有这些致命异常的消息也会被拒绝且不重新排队。这些异常通常在调用监听器之前发生,因此监听器没有机会对消息进行 ack 或 nack,消息就以未确认(un-acked)状态留在队列中。要恢复之前的行为,请将 ConditionalRejectingErrorHandler 上的 rejectManual 属性设置为 false 。 |