异常处理

RabbitMQ Java 客户端的许多操作可能会抛出受检异常。例如,在许多情况下可能会抛出 IOException 实例。RabbitTemplateSimpleMessageListenerContainer 和其他 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