異常處理
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 。 |