2.3. 异常处理
摘要
						Apache Camel 提供了几种不同的机制,它可让您以不同的粒度级别处理异常:您可以使用 doTry、doCatch 来处理路由内的异常,并最终 执行;或者,您可以指定每个异常类型执行哪些操作,并将这些规则应用于使用 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 队列(充当死信队列)。
					
XML DSL 示例 复制链接链接已复制到粘贴板!
						前面的示例也可以在 XML DSL 中表达,使用 onException 元素来定义 exception 子句,如下所示:
					
Trapping 多个例外 复制链接链接已复制到粘贴板!
						您可以定义多个 onException 子句,以在 RouteBuilder 范围内捕获异常。这可让您采取不同的操作来响应不同的例外。例如,Java DSL 中定义的以下一系列 onException 子句定义了 ValidationException、IOException 和 Exception 的不同死信目的地:
					
onException(ValidationException.class).to("activemq:validationFailed");
onException(java.io.IOException.class).to("activemq:ioExceptions");
onException(Exception.class).to("activemq:exceptions");
onException(ValidationException.class).to("activemq:validationFailed");
onException(java.io.IOException.class).to("activemq:ioExceptions");
onException(Exception.class).to("activemq:exceptions");
						您可以在 XML DSL 中定义相同系列的 onException 子句,如下所示:
					
						您也可以将多个例外分组为由相同的 onException 子句进行捕获。在 Java DSL 中,您可以对多个例外进行分组,如下所示:
					
onException(ValidationException.class, BuesinessException.class)
  .to("activemq:validationFailed");
onException(ValidationException.class, BuesinessException.class)
  .to("activemq:validationFailed");
						在 XML DSL 中,您可以通过定义 onException 元素中的多个 异常 元素来组合多个例外,如下所示:
					
<onException>
    <exception>com.mycompany.ValidationException</exception>
    <exception>com.mycompany.BuesinessException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
<onException>
    <exception>com.mycompany.ValidationException</exception>
    <exception>com.mycompany.BuesinessException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
						当捕获多个异常时,在 Exception 子句的次序上 非常重要。Apache Camel 最初会尝试将引发异常与 first 子句匹配。如果第一个子句无法匹配,则尝试下一个 onException 子句,直到找到匹配项为止。每个匹配尝试都会受以下算法的管控:
					
如果引发异常是 链的异常 (即,捕获异常并作为不同的异常),则最常见的异常类型最初是匹配的基础。这个例外测试如下:
- 
										如果 exception-to-test 正好在 
onException子句中指定的类型(使用instanceof测试),则会触发匹配项。 - 
										如果 exception-to-test 是 
onException子句中指定的类型的子类型,则会触发匹配项。 
- 
										如果 exception-to-test 正好在 
 - 如果最嵌套的异常无法获得匹配项,则将测试链(嵌套异常)中的下一个异常。测试会继续链,直到触发匹配或链用尽。
 
通过 throwException EIP,您可以从简单语言表达式创建新异常实例。您可以根据当前交换中的可用信息使其动态使用。例如,
<throwException exceptionType="java.lang.IllegalArgumentException" message="${body}"/>
<throwException exceptionType="java.lang.IllegalArgumentException" message="${body}"/>
字母频道 复制链接链接已复制到粘贴板!
						目前,在使用 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");
onException(ValidationException.class)
  .useOriginalMessage()
  .to("activemq:validationFailed");
						在 XML DSL 中,您可以通过设置 onException 元素上的 useOriginalMessage 属性来检索原始消息,如下所示:
					
<onException useOriginalMessage="true">
    <exception>com.mycompany.ValidationException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
<onException useOriginalMessage="true">
    <exception>com.mycompany.ValidationException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
							如果 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");
onException(ValidationException.class)
  .maximumRedeliveries(6)
  .retryAttemptedLogLevel(org.apache.camel.LogginLevel.WARN)
  .to("activemq:validationFailed");
						在 XML DSL 中,通过设置 redeliveryPolicy 元素上的属性来指定重新传送策略选项。例如,前面的路由可以在 XML DSL 中表示,如下所示:
					
<onException useOriginalMessage="true">
    <exception>com.mycompany.ValidationException</exception>
    <redeliveryPolicy maximumRedeliveries="6"/>
    <to uri="activemq:validationFailed"/>
</onException>
<onException useOriginalMessage="true">
    <exception>com.mycompany.ValidationException</exception>
    <redeliveryPolicy maximumRedeliveries="6"/>
    <to uri="activemq:validationFailed"/>
</onException>
重新传送选项的后,redelivery 选项的后后,才会被处理,直到最后一次重新传送尝试失败后才会被处理。有关所有重新传送选项的详情,请参考 第 6.3 节 “死信频道”。
						另外,您可以在 redeliveryPolicyProfile 实例中指定重新传送策略选项。然后,您可以使用 onException 元素的 redeliverPolicyRef 属性引用 redeliveryPolicyProfile 实例。例如,前面的路由可以表达如下:
					
							如果要使用多个 onException 子句中的重新传送策略,则使用 redeliveryPolicyProfile 的方法很有用。
						
条件 trapping 复制链接链接已复制到粘贴板!
						通过指定   进行异常捕获。如果您在 onWhen 选项,可以对ExceptiononException 子句中指定 onWhen 选项,则仅在引发异常与 子句匹配时触发匹配,而 onWhen predicate 在当前交换上评估为 true。
					
						例如,在以下 Java DSL 片段中,第一个 onException 子句触发器(仅当引发异常与 MyUserException 匹配并且当前交换 中的用户 标头为非null)时:
					
						前面的 onException 子句可以在 XML DSL 中表示,如下所示:
					
处理异常 复制链接链接已复制到粘贴板!
						默认情况下,当在路由期间引发异常时,会中断当前交换的处理,并在路由开始时将引发异常传播到消费者端点。触发了 onException 子句时,其行为基本上是相同的,但 onException 子句在请求引发异常前执行一些处理。
					
						但这种 默认行为是 处理异常的唯一方法。onException 提供了各种选项来修改异常处理行为,如下所示:
					
- 
								抑制异常 rethrow cnf-you 选项,您可以选择在 
Exception子句后禁止重新增长异常。换句话说,在这种情况下,异常 不会 传播到路由开始时的使用者端点。 - 继续处理 WWN-you 选项可以从最初发生异常时恢复正常处理交换。隐式,这种方法也会阻止重新浏览异常。
 - 如果路由开始时的消费者端点需要回复(即使用 InOut MEP),您可能更倾向于构建自定义故障回复消息,而不是传播异常到消费者端点。???
 
抑制异常重新箭头 复制链接链接已复制到粘贴板!
						要防止当前异常重新浏览并传播到消费者端点,您可以在 Java DSL 中将 handled () 选项设置为 true,如下所示:
					
onException(ValidationException.class)
  .handled(true)
  .to("activemq:validationFailed");
onException(ValidationException.class)
  .handled(true)
  .to("activemq:validationFailed");
						在 Java DSL 中,ded () 选项的参数可以是布尔值类型、predicate 类型或 Expression 类型(其中任何非布尔值表达式被解释为 true,如果它被评估为非空值)。
					
						同一路由可以配置为禁止使用 处理 元素的 XML DSL 中的 rerown 异常,如下所示:
					
继续处理 复制链接链接已复制到粘贴板!
						要从最初引发异常的路由中处理当前消息,您可以在 Java DSL 中将 continued 选项设为 true,如下所示:
					
onException(ValidationException.class) .continued(true);
onException(ValidationException.class)
  .continued(true);
						在 Java DSL 中,continued () 选项的参数可以是布尔值类型、predicate 类型或 Expression 类型(其中任何非布尔值表达式被解释为 true,如果它被评估为非空值)。
					
						同一路由可以在 XML DSL 中配置,使用 继续 元素,如下所示:
					
发送响应 复制链接链接已复制到粘贴板!
						当启动路由的消费者端点需要回复时,您可能更愿意构建自定义故障回复消息,而不是简单地将引发异常传播回消费者。在这种情况下,您需要执行两个基本步骤: 禁止 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");
// 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");
						如果您要向客户端发送错误响应,通常希望在响应中纳入异常消息的文本。您可以使用 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());
// 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());
						例外消息文本也可以通过 exception.message 变量从 Simple 语言访问。例如,您可以在回复消息中嵌入当前的异常文本,如下所示:
					
						前面的 onException 子句可以在 XML DSL 中表示,如下所示:
					
处理异常时的异常 复制链接链接已复制到粘贴板!
						在处理现有异常时(换句话说,一个在处理 onException 子句的过程中会引发异常)会以特殊方式处理。这种异常由特殊回退异常处理程序处理,它处理异常,如下所示:
					
- 所有现有的异常处理程序将被忽略,并立即处理失败。
 - 已记录新的异常。
 - 在 Exchange 对象上设置了新的异常。
 
						简单策略避免了复杂的故障情景,否则可能会出现 对Exception 子句被锁定在无限循环中。
					
范围 复制链接链接已复制到粘贴板!
						onException 子句可在以下任一范围内有效:
					
RouteBuilder 范围 TOKEN-
onException子句定义为RouteBuilder.configure ()方法内的独立声明,会影响该RouteBuilder实例中定义的所有路由。另一方面,这些onException子句 对任何其他RouteBuilder实例中定义的路由没有影响。在路由定义之前,必须 出现onException子句。所有指向此点的示例都使用
RouteBuilder范围来定义。- 
								路由范围 TOKEN- 
onException子句也可以直接嵌入到路由中。这些 onException 子句 仅影响 其中定义的路由。 
路由范围 复制链接链接已复制到粘贴板!
						您可以在路由定义的任意位置嵌入 onException 子句,但您必须使用 end () DSL 命令终止内嵌 onException 子句。
					
						例如,您可以在 Java DSL 中定义嵌入式 onException 子句,如下所示:
					
						您可以在 XML DSL 中定义嵌入式 onException 子句,如下所示: