概述
Apache Camel 是一个强大的开源集成框架,实现了众多已知的企业集成模式。
在使用Camel进行消息路由时,通常需要有效处理错误。为此,Camel提供了几种处理异常的方法。
在这篇教程中,我们将探讨两种在我们的Camel应用程序内部使用的异常处理策略。
依赖项
要开始,我们只需要将camel-spring-boot-starter
添加到我们的pom.xml
中:
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
创建路由
让我们首先定义一个故意抛出异常的相当基础的路由:
@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");
}
}
简单回顾一下,Camel中的一个路由是基本构建块,通常由Camel按顺序执行的一系列步骤组成,用于消费和处理消息。
在我们的示例中,我们配置路由从名为start
的直接端点消费消息。
然后,在新创建的Java DSL内线程的处理器中,我们故意抛出一个IllegalArgumentException
。
目前,我们的路由没有包含任何类型的异常处理,因此运行时,我们在应用的输出中会看到一些不太友好的内容:
...
10:21:57.087 [main] ERROR c.b.c.e.ExceptionThrowingRoute - Exception Thrown
10:21:57.094 [main] ERROR o.a.c.p.e.DefaultErrorHandler - 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 Processor@0x3e28af44 0
Stacktrace
---------------------------------------------------------------------------------------------------------------------------------------
java.lang.IllegalArgumentException: An exception happened on purpose
...
使用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()
块之后运行的代码。
此外,我们应注意到在Java DSL中,调用end()
方法表示块的结束非常重要。
Camel还提供了一个功能,允许我们在使用doCatch()
块时与谓词一起工作:
...
.doCatch(IOException.class, IllegalArgumentException.class).onWhen(exceptionMessage().contains("Hello"))
.to("mock:catch")
...
在这里,我们在运行时添加了一个谓词来决定是否触发catch块。在这种情况下,只有当引发的异常消息包含单词"Hello"时,我们才会触发它。很酷!
处理异常条款
不幸的是,先前方法的一个限制是它仅适用于单个路由。
随着应用程序的增长,当我们添加越来越多的路由时,我们可能不想逐个路由处理异常。这可能会导致代码重复,我们可能希望为应用程序有一个通用的错误处理策略。
幸运的是,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上找到。