錯誤處理
如本手冊開頭 概述 中所述,像 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 將傳送到該通道。否則,錯誤處理器將傳送到名為 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 提供了 ErrorMessagePublisher 和 ErrorMessageStrategy。您可以將它們用作釋出 ErrorMessage 例項的通用機制。您可以在任何錯誤處理場景中呼叫或擴充套件它們。ErrorMessageSendingRecoverer 將此類別擴充套件為 RecoveryCallback 實現,可與重試一起使用,例如 RequestHandlerRetryAdvice。ErrorMessageStrategy 用於根據提供的異常和 AttributeAccessor 上下文構建 ErrorMessage。它可以注入到任何 MessageProducerSupport 或 MessagingGatewaySupport 中。requestMessage 儲存在 AttributeAccessor 上下文中的 ErrorMessageUtils.INPUT_MESSAGE_CONTEXT_KEY 下。ErrorMessageStrategy 可以將該 requestMessage 用作其建立的 ErrorMessage 的 originalMessage 屬性。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)。
另請參閱 錯誤處理示例 以獲取更多資訊。