Apache Camel 異常處理
一、概述
Apache Camel 是一個強大的開源集成框架,實現了幾種已知的企業集成模式。
通常在使用 Camel 處理消息路由時,我們需要一種有效處理錯誤的方法。為此,Camel 提供了幾種處理異常的策略。
在本教程中,我們將介紹兩種可用於在 Camel 應用程序中進行異常處理的方法。
2. 依賴
我們需要開始的只是添加到我們的pom.xml
中的[camel-spring-boot-starter](https://search.maven.org/search?q=a:camel-spring-boot-starter)
:
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>3.19.0</version>
</dependency>
3. 創建路線
讓我們從定義一個故意拋出異常的相當基本的路由開始:
@Component
public class ExceptionThrowingRoute extends RouteBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionThrowingRoute.class);
@Override
public void configure() throws Exception {
from("direct:start-exception")
.routeId("exception-handling-route")
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
LOGGER.error("Exception Thrown");
throw new IllegalArgumentException("An exception happened on purpose");
}
}).to("mock:received");
}
}
快速回顧一下,Apache Camel 中的路由是一個基本的構建塊,通常由一系列步驟組成,由 Camel 按順序執行,用於消費和處理消息。
正如我們在簡單示例中所見,我們將路由配置為使用來自名為 start 的直接端點的消息。
然後,我們從一個新的Processor,
拋出一個IllegalArgumentException
,我們使用 Java DSL 在我們的路由中創建內聯。
目前,我們的路由不包含任何異常處理,所以當我們運行它時,我們會在應用程序的輸出中看到一些醜陋的東西:
...
10:21:57.087 [main] ERROR cbceExceptionThrowingRoute - Exception Thrown
10:21:57.094 [main] ERROR oacpeDefaultErrorHandler - Failed delivery for (MessageId: 50979CFF47E7816-0000000000000000 on ExchangeId: 50979CFF47E7816-0000000000000000).
Exhausted after delivery attempt: 1 caught: java.lang.IllegalArgumentException: An exception happened on purpose
Message History (source location and message history is disabled)
---------------------------------------------------------------------------------------------------------------------------------------
Source ID Processor Elapsed (ms)
exception-handling-route/excep from[direct://start-exception] 11
...
exception-handling-route/proce [email protected] 0
Stacktrace
---------------------------------------------------------------------------------------------------------------------------------------
java.lang.IllegalArgumentException: An exception happened on purpose
...
4. 使用doTry()
塊
現在讓我們繼續為我們的路由添加一些異常處理。在本節中,我們將看一下 Camel 的doTry()
塊,我們可以將其視為 Java 等效的try catch finally
,但直接嵌入在 DSL 中。
但首先,為了幫助簡化我們的代碼,我們將定義一個拋出IllegalArgumentException –
這將使我們的代碼更具可讀性,並且我們可以稍後在其他路線中重用我們的處理器:
@Component
public class IllegalArgumentExceptionThrowingProcessor implements Processor {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionLoggingProcessor.class);
@Override
public void process(Exchange exchange) throws Exception {
LOGGER.error("Exception Thrown");
throw new IllegalArgumentException("An exception happened on purpose");
}
}
有了我們的新處理器,讓我們在我們的第一個異常處理路線中使用它:
@Component
public class ExceptionHandlingWithDoTryRoute extends RouteBuilder {
@Override
public void configure() throws Exception {
from("direct:start-handling-exception")
.routeId("exception-handling-route")
.doTry()
.process(new IllegalArgumentExceptionThrowingProcessor())
.to("mock:received")
.doCatch(IOException.class, IllegalArgumentException.class)
.to("mock:caught")
.doFinally()
.to("mock:finally")
.end();
}
}
正如我們所看到的,我們路線中的代碼非常不言自明。我們基本上是在模仿使用 Camel 等效項的常規 Java try catch finally
語句。
但是,讓我們來看看我們路線的關鍵部分:
- 首先,我們使用
doTry()
方法包圍我們希望立即捕獲拋出的異常的路由部分 - 接下來,我們使用
doCatch
方法關閉這個塊。請注意,我們可以傳遞我們希望捕獲的不同異常類型的列表 - 最後,我們調用
doFinally(),
它定義了總是在doTry()
和任何doCatch()
塊之後運行的代碼
此外,我們應該注意到調用end()
方法來表示 Java DSL 中塊的結束是很重要的。
Camel 還提供了另一個強大的功能,可以讓我們在使用doCatch()
塊時使用謂詞:
...
.doCatch(IOException.class, IllegalArgumentException.class).onWhen(exceptionMessage().contains("Hello"))
.to("mock:catch")
...
這裡我們添加一個運行時謂詞來確定是否應該觸發 catch 塊。在這種情況下,我們只想在引發的異常消息包含單詞 Hello 時觸發它。很酷!
5. 使用例外條款
不幸的是,前一種方法的局限性之一是它僅適用於單一路線。
通常隨著我們的應用程序的增長和我們添加越來越多的路由,我們可能不希望逐個路由處理異常。這可能會導致重複代碼,我們可能需要為我們的應用程序提供一個通用的錯誤處理策略。
值得慶幸的是,Camel 通過 Java DSL 提供了一個異常子句機制來指定我們需要基於每個異常類型或全局的錯誤處理:
假設我們想為我們的應用程序實現一個異常處理策略。對於我們的簡單示例,我們假設我們只有一條路線:
@Component
public class ExceptionHandlingWithExceptionClauseRoute extends RouteBuilder {
@Autowired
private ExceptionLoggingProcessor exceptionLogger;
@Override
public void configure() throws Exception {
onException(IllegalArgumentException.class).process(exceptionLogger)
.handled(true)
.to("mock:handled")
from("direct:start-exception-clause")
.routeId("exception-clause-route")
.process(new IllegalArgumentExceptionThrowingProcessor())
.to("mock:received");
}
}
正如我們所看到的,我們使用onException
方法來處理IllegalArgumentException
發生時並應用一些特定的處理。
對於我們的示例,我們將處理傳遞給自定義的ExceptionLoggingProcessor
類,該類僅記錄消息頭。最後,我們使用handled(true)
方法將消息交換標記為已處理,然後將結果發送到名為handled
的模擬端點。
但是,我們應該注意,在 Camel 中,我們代碼的全局範圍是每個RouteBuilder
實例。因此,如果我們想通過多個RouteBuilder
類共享這個異常處理代碼,我們可以使用以下技術。
只需創建一個基本的抽象RouteBuilder
類並將錯誤處理邏輯放在其configure
方法中。
隨後,我們可以簡單地擴展這個類並確保我們調用了super.configure()
方法。本質上,我們只是在使用 Java 繼承技術。
六,結論
在本文中,我們學習瞭如何處理路由中的異常。首先,我們創建了一個簡單的 Camel 應用程序,其中包含幾個學習異常的途徑。
然後我們了解了使用doTry()
和doCatch()
塊語法以及後來的onException()
子句的兩種具體方法。
與往常一樣,本文的完整源代碼可在 GitHub 上獲得。