錯誤處理

正如本手冊開頭的概覽所述,像 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 的載荷。然後,該 Message 會被髮送到一個訊息通道,該通道的解析方式類似於 'replyChannel' 的解析。首先,如果在發生 Exception 時正在處理的請求 Message 包含 'errorChannel' 頭(頭名稱在 MessageHeaders.ERROR_CHANNEL 常量中定義),則 ErrorMessage 會發送到該通道。否則,錯誤處理器會發送到 bean 名稱為 errorChannel 的“全域性”通道(這也定義為一個常量:IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME)。

框架內部會建立一個預設的 errorChannel 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。如果您訂閱了可能丟擲異常的額外消費端點,並且不希望搶佔日誌記錄,請確保這些額外處理器的順序更高。

這裡最重要的一點是,基於訊息的錯誤處理僅適用於在 TaskExecutor 內執行的 Spring Integration 任務丟擲的異常。這不適用於與傳送者在同一執行緒中操作的處理器丟擲的異常(例如,透過本節前面描述的 DirectChannel)。

當計劃的輪詢任務執行中發生異常時,這些異常也會被封裝在 ErrorMessage 例項中併發送到 'errorChannel'。這是透過注入到全域性 taskScheduler bean 中的 MessagePublishingErrorHandler 來完成的。如果仍然需要使用標準的 'errorChannel' 整合流邏輯進行錯誤處理,建議對任何自定義 taskScheduler 使用該 MessagePublishingErrorHandler。在這種情況下,可以使用註冊的 integrationMessagePublishingErrorHandler bean。

要啟用全域性錯誤處理,請在該通道上註冊一個處理器。例如,您可以將 Spring Integration 的 ErrorMessageExceptionTypeRouter 配置為訂閱了 errorChannel 的端點的處理器。該路由可以根據 Exception 型別將錯誤訊息分發到多個通道。

從版本 4.3.10 開始,Spring Integration 提供了 ErrorMessagePublisherErrorMessageStrategy。您可以將它們用作釋出 ErrorMessage 例項的通用機制。您可以在任何錯誤處理場景中呼叫或擴充套件它們。ErrorMessageSendingRecoverer 擴充套件了此類,作為 RecoveryCallback 實現,可用於重試,例如RequestHandlerRetryAdviceErrorMessageStrategy 用於根據提供的異常和 AttributeAccessor 上下文構建 ErrorMessage。它可以注入到任何 MessageProducerSupportMessagingGatewaySupport 中。requestMessageAttributeAccessor 上下文中的 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)。

另請參見錯誤處理示例獲取更多資訊。