異常處理

RabbitMQ Java 客戶端的許多操作可能會丟擲受檢異常(checked exception)。例如,在許多情況下可能會丟擲 IOException 例項。RabbitTemplateSimpleMessageListenerContainer 和其他 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