2.3. 异常处理


摘要

Apache Camel 提供了几种不同的机制,它可让您以不同的粒度级别处理异常:您可以使用 doTrydoCatch 来处理路由内的异常,并最终 执行;或者,您可以指定每个异常类型执行哪些操作,并将这些规则应用于使用 Exception 的 RouteBuilder 中所有路由中的所有路由 ; 或者,您可以指定 所有 异常类型执行的操作,并使用 错误处理程序 将这一规则应用于 RouteBuilder 中的所有路由。

有关异常处理的详情,请参考 第 6.3 节 “死信频道”

2.3.1. onException Clause

概述

onException 子句是捕获异常的一种强大的机制,可在一个或多个路由中发生:它特定于类型,使您能够定义不同的操作来处理不同的异常类型;它允许您定义使用相同(实际上,稍有的扩展)语法的操作,作为路由,从而在您处理异常情况下,以方便您处理异常的方式;它基于一个捕获模型,这基于一个结果。

使用 onException 的 Trapping 异常

onException 子句是捕获异常的机制。也就是说,一旦定义了 onException 子句,它将陷阱在路由中的任何点上发生异常。这与捕获异常的 Java 试用/分散机制不同,只有在将特定的代码片段 明确 包含在 try 块中时。

当您定义 onException 子句时,Apache Camel 运行时将每个路由节点隐式包含在 try 块中时,会出现什么情况。这就是为什么 onException 子句可以在路由中的任何时间点捕捉异常。但此换行可自动为您完成;路由定义中不可见。

Java DSL 示例

在以下 Java DSL 示例中,onException 子句应用到 RouteBuilder 类中定义的所有路由。如果在处理其中任一路由(来自"seda:inputA")来自("seda:inputB")时出现 ValidationException 异常,则 onException 子句将当前的交换重定向到 verify Failed JMS 队列(充当死信队列)。

// Java
public class MyRouteBuilder extends RouteBuilder {

  public void configure() {
    onException(ValidationException.class)
      .to("activemq:validationFailed");

    from("seda:inputA")
      .to("validation:foo/bar.xsd", "activemq:someQueue");

    from("seda:inputB").to("direct:foo")
      .to("rnc:mySchema.rnc", "activemq:anotherQueue");
  }
}
Copy to Clipboard Toggle word wrap

XML DSL 示例

前面的示例也可以在 XML DSL 中表达,使用 onException 元素来定义 exception 子句,如下所示:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:camel="http://camel.apache.org/schema/spring"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <onException>
            <exception>com.mycompany.ValidationException</exception>
            <to uri="activemq:validationFailed"/>
        </onException>
        <route>
            <from uri="seda:inputA"/>
            <to uri="validation:foo/bar.xsd"/>
            <to uri="activemq:someQueue"/>
        </route>
        <route>
            <from uri="seda:inputB"/>
            <to uri="rnc:mySchema.rnc"/>
            <to uri="activemq:anotherQueue"/>
        </route>
    </camelContext>

</beans>
Copy to Clipboard Toggle word wrap

Trapping 多个例外

您可以定义多个 onException 子句,以在 RouteBuilder 范围内捕获异常。这可让您采取不同的操作来响应不同的例外。例如,Java DSL 中定义的以下一系列 onException 子句定义了 ValidationExceptionIOExceptionException 的不同死信目的地:

onException(ValidationException.class).to("activemq:validationFailed");
onException(java.io.IOException.class).to("activemq:ioExceptions");
onException(Exception.class).to("activemq:exceptions");
Copy to Clipboard Toggle word wrap

您可以在 XML DSL 中定义相同系列的 onException 子句,如下所示:

<onException>
    <exception>com.mycompany.ValidationException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
<onException>
    <exception>java.io.IOException</exception>
    <to uri="activemq:ioExceptions"/>
</onException>
<onException>
    <exception>java.lang.Exception</exception>
    <to uri="activemq:exceptions"/>
</onException>
Copy to Clipboard Toggle word wrap

您也可以将多个例外分组为由相同的 onException 子句进行捕获。在 Java DSL 中,您可以对多个例外进行分组,如下所示:

onException(ValidationException.class, BuesinessException.class)
  .to("activemq:validationFailed");
Copy to Clipboard Toggle word wrap

在 XML DSL 中,您可以通过定义 onException 元素中的多个 异常 元素来组合多个例外,如下所示:

<onException>
    <exception>com.mycompany.ValidationException</exception>
    <exception>com.mycompany.BuesinessException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
Copy to Clipboard Toggle word wrap

当捕获多个异常时,在 Exception 子句的次序上 非常重要。Apache Camel 最初会尝试将引发异常与 first 子句匹配。如果第一个子句无法匹配,则尝试下一个 onException 子句,直到找到匹配项为止。每个匹配尝试都会受以下算法的管控:

  1. 如果引发异常是 链的异常 (即,捕获异常并作为不同的异常),则最常见的异常类型最初是匹配的基础。这个例外测试如下:

    1. 如果 exception-to-test 正好在 onException 子句中指定的类型(使用 instanceof测试),则会触发匹配项。
    2. 如果 exception-to-test 是 onException 子句中指定的类型的子类型,则会触发匹配项。
  2. 如果最嵌套的异常无法获得匹配项,则将测试链(嵌套异常)中的下一个异常。测试会继续链,直到触发匹配或链用尽。
注意

通过 throwException EIP,您可以从简单语言表达式创建新异常实例。您可以根据当前交换中的可用信息使其动态使用。例如,

<throwException exceptionType="java.lang.IllegalArgumentException" message="${body}"/>
Copy to Clipboard Toggle word wrap

字母频道

目前,在使用 onException 的基本示例都利用了 死信频道 模式。也就是说,当 onException 子句捕获异常时,当前交换将路由到特殊目的地( deadletter 频道)。deadletter 频道充当未处理的失败消息的 存放 区域。管理员可以稍后检查消息,并决定需要采取什么操作。

有关死信频道模式的详情,请参考 第 6.3 节 “死信频道”

使用原始消息

当路由期间出现异常时,交换中的消息可能已被大量修改(甚至不能被人读取)。通常,管理员更容易决定要执行哪些正确性操作,如果死信队列中可见的消息是 原始消息,如路由开始时收到的消息。useOriginalMessage 选项默认为 false,但如果在错误处理程序上配置,则会自动启用。

注意

使用OriginalMessage 选项可导致应用到 Camel 路由时出现意外行为,该路由将消息发送到多个端点,或将消息拆分为部分。原始消息可能无法在多广播、斯plitter 或 RecipientList 路由中保留,其中中间处理步骤会修改原始消息。

在 Java DSL 中,您可以将交换中的消息替换为原始消息。将 setAllowUseOriginalMessage () 设置为 true,然后使用 useOriginalMessage () DSL 命令,如下所示:

onException(ValidationException.class)
  .useOriginalMessage()
  .to("activemq:validationFailed");
Copy to Clipboard Toggle word wrap

在 XML DSL 中,您可以通过设置 onException 元素上的 useOriginalMessage 属性来检索原始消息,如下所示:

<onException useOriginalMessage="true">
    <exception>com.mycompany.ValidationException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
Copy to Clipboard Toggle word wrap
注意

如果 setAllowUseOriginalMessage () 选项被设置为 true,Camel 会在路由开始时复制原始消息,以确保在 使用OriginalMessage () 时原始消息可用。但是,如果在 Camel 上下文上将 setAllowUseOriginalMessage () 选项设为 false (这是默认设置 ),则原始消息将无法访问,您无法调用 useOriginalMessage ()

利用默认行为的一个原因是,在处理大型消息时优化性能。

在 2.18 之前的 Camel 版本中,allowUseOriginalMessage 的默认设置是 true。

重新传送策略

Apache Camel 为您提供尝试 除发生异常的情况,而不是中断消息处理,并在出现异常时立即放弃信息。在网络系统中,超时发生且临时故障发生时,通常可能会成功处理失败的消息,如果在引发原始异常后马上进行恢复。

Apache Camel 重新发送支持在异常发生后保存消息的各种策略。配置重新传送的一些最重要的选项如下:

maximumRedeliveries()
指定可尝试重新传送的次数(默认为 0)。负值意味着始终尝试重新发送(等同于无限值)。
retryWhile()

指定谓词( Predicate )类型,它决定 Apache Camel 是否存在继续恢复。如果 predicate 在当前交换上评估为 true,则尝试重新发送;否则,重新传送将停止,且不会进行进一步重新发送尝试。

这个选项优先于 maximumRedeliveries () 选项。

在 Java DSL 中,重新传送策略选项使用 onException 子句中的 DSL 命令来指定。例如,您可以指定最多 6 个回收,之后该交换会发送到 validationFailed deadletter 队列,如下所示:

onException(ValidationException.class)
  .maximumRedeliveries(6)
  .retryAttemptedLogLevel(org.apache.camel.LogginLevel.WARN)
  .to("activemq:validationFailed");
Copy to Clipboard Toggle word wrap

在 XML DSL 中,通过设置 redeliveryPolicy 元素上的属性来指定重新传送策略选项。例如,前面的路由可以在 XML DSL 中表示,如下所示:

<onException useOriginalMessage="true">
    <exception>com.mycompany.ValidationException</exception>
    <redeliveryPolicy maximumRedeliveries="6"/>
    <to uri="activemq:validationFailed"/>
</onException>
Copy to Clipboard Toggle word wrap

重新传送选项的后,redelivery 选项的后后,才会被处理,直到最后一次重新传送尝试失败后才会被处理。有关所有重新传送选项的详情,请参考 第 6.3 节 “死信频道”

另外,您可以在 redeliveryPolicyProfile 实例中指定重新传送策略选项。然后,您可以使用 onException 元素的 redeliverPolicyRef 属性引用 redeliveryPolicyProfile 实例。例如,前面的路由可以表达如下:

<redeliveryPolicyProfile id="redelivPolicy" maximumRedeliveries="6" retryAttemptedLogLevel="WARN"/>

<onException useOriginalMessage="true" redeliveryPolicyRef="redelivPolicy">
    <exception>com.mycompany.ValidationException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
Copy to Clipboard Toggle word wrap
注意

如果要使用多个 onException 子句中的重新传送策略,则使用 redeliveryPolicyProfile 的方法很有用。

条件 trapping

通过指定 onWhen 选项,可以对Exception 进行异常捕获。如果您在 onException 子句中指定 onWhen 选项,则仅在引发异常与 子句匹配时触发匹配,而 onWhen predicate 在当前交换上评估为 true

例如,在以下 Java DSL 片段中,第一个 onException 子句触发器(仅当引发异常与 MyUserException 匹配并且当前交换 中的用户 标头为非null)时:

// Java

// Here we define onException() to catch MyUserException when
// there is a header[user] on the exchange that is not null
onException(MyUserException.class)
    .onWhen(header("user").isNotNull())
    .maximumRedeliveries(2)
    .to(ERROR_USER_QUEUE);

// Here we define onException to catch MyUserException as a kind
// of fallback when the above did not match.
// Noitce: The order how we have defined these onException is
// important as Camel will resolve in the same order as they
// have been defined
onException(MyUserException.class)
    .maximumRedeliveries(2)
    .to(ERROR_QUEUE);
Copy to Clipboard Toggle word wrap

前面的 onException 子句可以在 XML DSL 中表示,如下所示:

<redeliveryPolicyProfile id="twoRedeliveries" maximumRedeliveries="2"/>

<onException redeliveryPolicyRef="twoRedeliveries">
    <exception>com.mycompany.MyUserException</exception>
    <onWhen>
        <simple>${header.user} != null</simple>
    </onWhen>
    <to uri="activemq:error_user_queue"/>
</onException>

<onException redeliveryPolicyRef="twoRedeliveries">
    <exception>com.mycompany.MyUserException</exception>
    <to uri="activemq:error_queue"/>
</onException>
Copy to Clipboard Toggle word wrap

处理异常

默认情况下,当在路由期间引发异常时,会中断当前交换的处理,并在路由开始时将引发异常传播到消费者端点。触发了 onException 子句时,其行为基本上是相同的,但 onException 子句在请求引发异常前执行一些处理。

但这种 默认行为是 处理异常的唯一方法。onException 提供了各种选项来修改异常处理行为,如下所示:

  • 抑制异常 rethrow cnf-you 选项,您可以选择在 Exception 子句后禁止重新增长异常。换句话说,在这种情况下,异常 不会 传播到路由开始时的使用者端点。
  • 继续处理 WWN-you 选项可以从最初发生异常时恢复正常处理交换。隐式,这种方法也会阻止重新浏览异常。
  • 如果路由开始时的消费者端点需要回复(即使用 InOut MEP),您可能更倾向于构建自定义故障回复消息,而不是传播异常到消费者端点。???

抑制异常重新箭头

要防止当前异常重新浏览并传播到消费者端点,您可以在 Java DSL 中将 handled () 选项设置为 true,如下所示:

onException(ValidationException.class)
  .handled(true)
  .to("activemq:validationFailed");
Copy to Clipboard Toggle word wrap

在 Java DSL 中,ded () 选项的参数可以是布尔值类型、predicate 类型或 Expression 类型(其中任何非布尔值表达式被解释为 true,如果它被评估为非空值)。

同一路由可以配置为禁止使用 处理 元素的 XML DSL 中的 rerown 异常,如下所示:

<onException>
    <exception>com.mycompany.ValidationException</exception>
    <handled>
        <constant>true</constant>
    </handled>
    <to uri="activemq:validationFailed"/>
</onException>
Copy to Clipboard Toggle word wrap

继续处理

要从最初引发异常的路由中处理当前消息,您可以在 Java DSL 中将 continued 选项设为 true,如下所示:

onException(ValidationException.class)
  .continued(true);
Copy to Clipboard Toggle word wrap

在 Java DSL 中,continued () 选项的参数可以是布尔值类型、predicate 类型或 Expression 类型(其中任何非布尔值表达式被解释为 true,如果它被评估为非空值)。

同一路由可以在 XML DSL 中配置,使用 继续 元素,如下所示:

<onException>
    <exception>com.mycompany.ValidationException</exception>
    <continued>
        <constant>true</constant>
    </continued>
</onException>
Copy to Clipboard Toggle word wrap

发送响应

当启动路由的消费者端点需要回复时,您可能更愿意构建自定义故障回复消息,而不是简单地将引发异常传播回消费者。在这种情况下,您需要执行两个基本步骤: 禁止 rethrown exception 使用 handled 选项;并使用自定义故障消息填充交换的 Out 消息插槽。

例如,以下 Java DSL 片段演示了如何在出现 MyFunctionalException 异常时发送包含文本字符串 Sorry 的回复消息:

// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client)
// but we want to return a fixed text response, so we transform OUT body as Sorry.
onException(MyFunctionalException.class)
    .handled(true)
    .transform().constant("Sorry");
Copy to Clipboard Toggle word wrap

如果您要向客户端发送错误响应,通常希望在响应中纳入异常消息的文本。您可以使用 exceptionMessage () builder 方法访问当前异常消息的文本。例如,您可以在发生 MyFunctionalException 异常时发送包含异常消息文本的回复,如下所示:

// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client)
// but we want to return a fixed text response, so we transform OUT body and return the exception message
onException(MyFunctionalException.class)
    .handled(true)
    .transform(exceptionMessage());
Copy to Clipboard Toggle word wrap

例外消息文本也可以通过 exception.message 变量从 Simple 语言访问。例如,您可以在回复消息中嵌入当前的异常文本,如下所示:

// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client)
// but we want to return a fixed text response, so we transform OUT body and return a nice message
// using the simple language where we want insert the exception message
onException(MyFunctionalException.class)
    .handled(true)
    .transform().simple("Error reported: ${exception.message} - cannot process this message.");
Copy to Clipboard Toggle word wrap

前面的 onException 子句可以在 XML DSL 中表示,如下所示:

<onException>
    <exception>com.mycompany.MyFunctionalException</exception>
    <handled>
        <constant>true</constant>
    </handled>
    <transform>
        <simple>Error reported: ${exception.message} - cannot process this message.</simple>
    </transform>
</onException>
Copy to Clipboard Toggle word wrap

处理异常时的异常

在处理现有异常时(换句话说,一个在处理 onException 子句的过程中会引发异常)会以特殊方式处理。这种异常由特殊回退异常处理程序处理,它处理异常,如下所示:

  • 所有现有的异常处理程序将被忽略,并立即处理失败。
  • 已记录新的异常。
  • 在 Exchange 对象上设置了新的异常。

简单策略避免了复杂的故障情景,否则可能会出现 对Exception 子句被锁定在无限循环中。

范围

onException 子句可在以下任一范围内有效:

  • RouteBuilder 范围 TOKEN- onException 子句定义为 RouteBuilder.configure () 方法内的独立声明,会影响该 RouteBuilder 实例中定义的所有路由。另一方面,这些 onException 子句 对任何其他 RouteBuilder 实例中定义的路由没有影响。在路由定义之前,必须 出现 onException 子句。

    所有指向此点的示例都使用 RouteBuilder 范围来定义。

  • 路由范围 TOKEN- onException 子句也可以直接嵌入到路由中。这些 onException 子句 仅影响 其中定义的路由。

路由范围

您可以在路由定义的任意位置嵌入 onException 子句,但您必须使用 end () DSL 命令终止内嵌 onException 子句。

例如,您可以在 Java DSL 中定义嵌入式 onException 子句,如下所示:

// Java
from("direct:start")
  .onException(OrderFailedException.class)
    .maximumRedeliveries(1)
    .handled(true)
    .beanRef("orderService", "orderFailed")
    .to("mock:error")
  .end()
  .beanRef("orderService", "handleOrder")
  .to("mock:result");
Copy to Clipboard Toggle word wrap

您可以在 XML DSL 中定义嵌入式 onException 子句,如下所示:

<route errorHandlerRef="deadLetter">
    <from uri="direct:start"/>
    <onException>
        <exception>com.mycompany.OrderFailedException</exception>
        <redeliveryPolicy maximumRedeliveries="1"/>
        <handled>
            <constant>true</constant>
        </handled>
        <bean ref="orderService" method="orderFailed"/>
        <to uri="mock:error"/>
    </onException>
    <bean ref="orderService" method="handleOrder"/>
    <to uri="mock:result"/>
</route>
Copy to Clipboard Toggle word wrap
返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat