錯誤處理

如本手冊最開始的概觀中所述,訊息導向框架(如 Spring Integration)背後的主要動機之一,是促進組件之間的鬆散耦合。訊息通道扮演著重要的角色,因為生產者和消費者不必互相了解。然而,這些優點也帶來了一些缺點。在鬆散耦合的環境中,有些事情變得更加複雜,其中一個例子是錯誤處理。

當發送訊息到通道時,最終處理該訊息的組件可能與發送者在同一個執行緒中運作,也可能不在。如果使用簡單的預設 DirectChannel(當 <channel> 元素沒有 <queue> 子元素且沒有 'task-executor' 屬性時),訊息處理會發生在發送初始訊息的同一個執行緒中。在這種情況下,如果拋出 Exception,它可以被發送者捕獲,或者如果它是未捕獲的 RuntimeException,它可能會傳播到發送者之外。這與一般 Java 呼叫堆疊中拋出異常的操作行為相同。

在呼叫者執行緒上執行的訊息流程可以透過訊息傳遞閘道器(請參閱訊息傳遞閘道器)或 MessagingTemplate(請參閱MessagingTemplate)調用。在任一種情況下,預設行為是將任何異常拋給呼叫者。對於訊息傳遞閘道器,請參閱錯誤處理,以了解異常如何拋出以及如何設定閘道器將錯誤路由到錯誤通道的詳細資訊。當直接使用 MessagingTemplate 或發送到 MessageChannel 時,異常總是會拋給呼叫者。

當新增非同步處理時,事情變得相當複雜。例如,如果 'channel' 元素確實提供了 'queue' 子元素(Java 和註解設定中的 QueueChannel),則處理訊息的組件會在與發送者不同的執行緒中運作。當使用 ExecutorChannel 時也是如此。發送者可能已將 Message 放入通道並繼續處理其他事情。使用標準的 Exception 拋出技術,無法將 Exception 直接拋回給發送者。相反地,處理非同步流程的錯誤需要錯誤處理機制也是非同步的。

Spring Integration 透過將錯誤發佈到訊息通道來支援其組件的錯誤處理。具體來說,Exception 會變成 Spring Integration ErrorMessage 的 payload。然後,該 Message 會被發送到一個訊息通道,該通道的解析方式與 'replyChannel' 解析類似。首先,如果在發生 Exception 時正在處理的要求 Message 包含 'errorChannel' 標頭(標頭名稱在 MessageHeaders.ERROR_CHANNEL 常數中定義),則 ErrorMessage 會被發送到該通道。否則,錯誤處理器會發送到一個「全域」通道,其 bean 名稱為 errorChannel(這也定義為常數:IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME)。

預設的 errorChannel bean 是由框架內部建立的。但是,如果您想控制設定,可以定義自己的 bean。以下範例顯示如何在 XML 設定中定義一個由容量為 500 的佇列支援的錯誤通道

  • Java

  • XML

@Bean
QueueChannel errorChannel() {
    return new QueueChannel(500);
}
<int:channel id="errorChannel">
    <int:queue capacity="500"/>
</int:channel>
預設的錯誤通道是 PublishSubscribeChannel。預設情況下,它有一個 LoggingHandler 作為訂閱者,具有 ERROR 記錄層級和訂閱順序為 Ordered.LOWEST_PRECEDENCE - 100。如果您訂閱可能會拋出異常的其他消費端點,並且您不希望搶先記錄,請確保其他處理器具有更高的順序。

這裡最重要的是要理解,基於訊息傳遞的錯誤處理僅適用於由 Spring Integration 任務拋出的異常,該任務在 TaskExecutor 中執行。這不適用於由與發送者在同一個執行緒中運作的處理器拋出的異常(例如,透過本節前面描述的 DirectChannel)。

當排程的 poller 任務執行中發生異常時,這些異常也會被包裝在 ErrorMessage 實例中,並發送到 'errorChannel'。這是透過注入到全域 taskScheduler bean 中的 MessagePublishingErrorHandler 來完成的。建議將 MessagePublishingErrorHandler 用於任何自訂 taskScheduler,如果錯誤處理仍然必須使用標準的 'errorChannel' 整合流程邏輯來完成。在這種情況下,可以使用已註冊的 integrationMessagePublishingErrorHandler bean。

若要啟用全域錯誤處理,請在該通道上註冊處理器。例如,您可以將 Spring Integration 的 ErrorMessageExceptionTypeRouter 設定為訂閱 errorChannel 的端點的處理器。然後,該路由器可以根據 Exception 類型將錯誤訊息分散到多個通道。

從 4.3.10 版開始,Spring Integration 提供了 ErrorMessagePublisherErrorMessageStrategy。您可以將它們用作發佈 ErrorMessage 實例的通用機制。您可以在任何錯誤處理場景中呼叫或擴充它們。ErrorMessageSendingRecoverer 將此類別擴充為 RecoveryCallback 實作,可用於重試,例如RequestHandlerRetryAdviceErrorMessageStrategy 用於根據提供的異常和 AttributeAccessor 上下文建立 ErrorMessage。它可以注入到任何 MessageProducerSupportMessagingGatewaySupport 中。requestMessage 儲存在 AttributeAccessor 上下文的 ErrorMessageUtils.INPUT_MESSAGE_CONTEXT_KEY 下。ErrorMessageStrategy 可以使用該 requestMessage 作為它建立的 ErrorMessageoriginalMessage 屬性。DefaultErrorMessageStrategy 正是這樣做的。

從 5.2 版開始,框架組件拋出的所有 MessageHandlingException 實例都包含組件 BeanDefinition 資源和來源,以確定異常的設定點。在 XML 設定的情況下,資源是 XML 檔案路徑,來源是帶有其 id 屬性的 XML 標籤。使用 Java 和註解設定,資源是 @Configuration 類別,來源是 @Bean 方法。在大多數情況下,目標整合流程解決方案是基於現成的組件及其設定選項。當執行階段發生異常時,堆疊追蹤中不會涉及任何終端使用者程式碼,因為執行是針對 bean,而不是它們的設定。包含 bean 定義的資源和來源有助於確定可能的設定錯誤,並提供更好的開發人員體驗。

從 5.4.3 版開始,預設錯誤通道設定了屬性 requireSubscribers = true,以避免在該通道上沒有訂閱者時(例如,當應用程式上下文停止時)靜默忽略訊息。在這種情況下,會拋出 MessageDispatchingException,這可能會導致輸入通道配接器的用戶端回呼否定確認(或回滾)來源系統中的原始訊息,以便重新傳遞或其他未來考量。若要還原先前的行為(忽略未分派的錯誤訊息),必須將全域整合屬性 spring.integration.channels.error.requireSubscribers 設定為 false。請參閱全域屬性PublishSubscribeChannel 設定(如果您手動設定全域 errorChannel)以取得更多資訊。

另請參閱錯誤處理範例以取得更多資訊。