異常處理
RabbitMQ Java 用戶端的許多操作都可能拋出受檢例外。例如,在許多情況下可能會拋出 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 版本開始,即使容器確認模式為 MANUAL,具有這些致命例外的訊息也會預設被拒絕且「不」重新排隊。這些例外通常在調用監聽器之前發生,因此監聽器沒有機會 ack 或 nack 訊息,因此它會保持在佇列中處於未確認狀態。若要還原為先前的行為,請將 ConditionalRejectingErrorHandler 上的 rejectManual 屬性設定為 false 。 |