camel系列-错误处理

在前面的章节中,你已经学到 Camel 将所有的异常认为是可恢复的错误,并使用setException(Throwable cause)方法将他们存储在 Exchange 中。这意味着 Camel 错误处理机制只会对设置到 Exchange 上的异常做出反应。默认情况下,如果一个不可恢复的错误设置为一个错误消息,此时 Camel 的错误处理机制不会做出反应。也就是说在exchange.getException() != null时,Camel 的错误处理机制才会触发。

Camel 提供一系列的错误处理器。他们在表中列出。

错误处理器 描述
DefaultErrorHandler 在没有配置其他错误处理器时,这是默认自动启用的错误处理器。
DeadLetterChannel 此错误处理器实现了死信通道企业集成模式。
TransactionErrorHandler 这是一个能够感知事务的错误处理器,扩展自默认的错误处理器。事务将在第九章中讲述,本章只会简短涉及到。在第九章中,我们会再次学习这个错误处理器。
NoErrorHandler 这个处理器用来完全禁用错误处理器。
LoggingErrorHandler 这个处理器只是记录异常日志。

乍一看,有五个错误处理程序似乎很复杂,但在大多数情况下你只会使用默认的错误处理器。

前三个错误处理器都继承自 RedeliveryErrorHandler 类。这个类包含了错误处理的主要逻辑。后两个错误处理程序功能有限,没有继承 RedeliveryErrorHandler 类。 
我们来看看每一个错误处理器。

默认的错误处理器

DefaultErrorHandler 是 Camel 默认的错误处理器,涵盖了 Camel 的大多数用例。要理解它,先看下面这个路由:

1
2
3
from("direct:newOrder")
.beanRef("orderService, "validate")
.beanRef("orderService, "store");

默认的错误处理器是预配置的,不需要在路由中显示声明。那么,如果 orderService bean 的 validate 方法中抛出一个异常,将会发生什么?

要回答这个问题,我们需要深入了解 Camel 内部的处理过程,以及错误处理所处的位置。在每一个 Camel 路由中,在路由图中的每两个节点中间,都有一个 Channel,如图 5.3 所示。

Channel 位于每个节点之间的路由路径上,确保它可以作为一个控制器,在运行时监视和控制路由。这是路由丰富特性(错误处理,消息跟踪,拦截器等等)之一。目前,你只需知道这是错误处理器所处的位置。

回到这个例子中路由上,假设从 orderService bean 的 validate 方法中抛出一个异常。就是说在图中,标为 3 的 Processor 将抛出一个异常,这个异常将传播给他前面的 channel,在这里,错误处理器将会捕获这个异常。这给了 Camel 做出相应反应的机会。Camel 可以再尝试一次(redeliver),或者可以将这个消息路由到其他路由路径上(使用异常策略进行绕道)。在默认配置下,Camel 会将异常传播给调用者。

默认的错误处理器有如下配置: 
1、No redelivery 不进行再次尝试  
2、异常传播给调用者  
这与你在 Java 中使用异常类似,所以 Camel 的终端用户不会对此感到惊奇。

让我们继续学习下一个错误处理器,DeadLetterChannel。

DeadLetterChannel 错误处理器

DeadLetterChannel 错误处理器与默认的错误处理器类似,除了以下不同点: 
1、DeadLetterChannel 是唯一一个支持将错误消息传送到死信队列的错误处理器; 
2、与默认错误处理器不同,DeadLetterChannel 在默认情况下会处理异常,并将失败消息传送到死信队列。 
3、当消息移动到死信队列时,DeadLetterChannel 支持使用原始输入消息。

让我们来看看这些不同点的更多细节。

DeadLetterChannel 错误处理器是一个实现了 Dead Letter Channel 企业集成模式规则的错误处理器。这个企业集成模式的涵义是:如果一个消息不能被处理或者被重新发送,此消息应该被传送到一个死信队列。图展示了这个模式。

正如你所看到的,标注 1 的消费者消费了一个新消息,此消息应该被路由到标注 3 的 Processor 上,标注 2 的 channel 是标注 1、3 之间的控制器,如果消息不能被发送到标注 3 上,channel 会调用 DeadLetterChannel 错误处理器,将消息传送到死信队列(标注 4)。这保证了消息的安全,并允许应用程序继续运行。 
这种模式通常与消息传递一起使用。它不是让一个失败的消息阻止新消息的传递,而是将失败的消息移除当前路由(转到死信队列)。

同样的概念也适用于 Camel 的死信通道错误处理程序。此错误处理程序与一个作为端点(endpoint)的死信队列相连,允许你选择使用任何 Camel 端点。例如,你可以使用数据库,文件或者只是打印出错误消息日志。 
当你选择使用 DeadLetterChannel 错误处理器,你必须配置死信队列作为一个 endpoint,这样错误处理器才知道将错误消息转移到什么地方。这一点在 java DSL 和 Spring XML 中的实现有些不同。例如,下面是将日志消息以 ERROR 级别输出的两种实现: 
errorHandler(deadLetterChannel(“log:dead?level=ERROR”));

现在,让我们看看死信通道错误处理程序在他将消息传到死信队列时是如何处理异常的。

默认的异常处理方式  
默认情况下,Camel 用制止异常的方式来处理它,将异常从 Exchange 中移除,然后将异常存储到 exchang 的 Properties 中。在消息传送到死信队列之后,Camel 停止路由此消息,调用者认为此消息被处理。 
当一个消息被传送到死信队列,你可以通过 exchange 的 Exchange.CAUSED_EXCEPTION 属性来得到异常:

1
Exception e = exchange.getProperty(Exchange.CAUSED_EXCEPTION,Exception.class);

现在让我们看看使用原始消息的方式。 
使用原始消息和死信通道  
假设你有一个路由,在这个路由中,在消息到达目的地之前,消息会经历一系列的处理步骤,每一步都会对消息进行修改,就像下面的代码这样:

1
2
3
4
5
6
errorHandler(deadLetterChannel("jms:queue:dead"));
from("jms:queue:inbox")
.beanRef("orderService", "decrypt")
.beanRef("orderService", "validate")
.beanRef("orderService", "enrich")
.to("jms:queue:order");

现在,假设 validate 方法抛出异常,deadLetterChannel 错误处理器将消息发送到死信队列。此时一个新消息到达,并在 enrich 方法中抛出异常,那么此消息被发送到同一个死信队列。如果你先重新发送这些消息,你能直接将他们放到 inbox 队列中吗?

理论上,你可以这么做,但是被发送到死信队列中的这些消息与到达 inbox 队列时的消息已经不再匹配了—-他们在路由中被修改了。你所需要的是将原始消息发送到死信队列,这样你就可以使用原始消息进行重新发送。 
useOriginalMessage 配置选项可以使 Camel 将原始消息发送到死信队列。使用 useOriginalMessage 配置错误处理器方式如下:

1
errorHandler(deadLetterChannel("jms:queue:dead").useOriginalMessage());

TransactionErrorHandler

TransactionErrorHandler 错误处理器基于默认的错误处理器创建,具有默认错误处理器所有的功能,而且他还支持事务路由。第九章将讲解事务并详细讨论这个错误处理器,所以在这里我们不再多说。你只需要知道有这个 TransactionErrorHandler 错误处理器,他是 Camel 核心的一部分。 
剩下的两个错误处理器很少使用,并且更简单。

NoErrorHandler

NoErrorHandler 用来禁用错误处理器。Camel 当前的体系结构要求必须配置一个错误处理器,所以,如果你想禁用错误处理,就需要提供一个空壳错误处理器,这就是 NoErrorHandler。

LoggingErrorHandler

LoggingErrorHandler 随异常记录失败消息的日志。日志记录器可以使用 log4j 或者 Java Util Logger。 
Camel 默认会使用 org.apache.camel.processor.LoggingErrorHandler 类,在 ERROR 级别来记录失败消息的日志。当然,你可以对其进行个性个配置。

学完了 Camel 提供的五个错误处理器,让我们来看下这些错误处理器的主要特性。

错误处理器的特性

默认错误处理器,DeadLetterChannel 和 TransactionErrorHandler 错误处理器都是基于 org.apache.camel.processor.RedeliveryErrorHandler 创建,所以他们都有共同的几个主要特性。表列出了这些特性。

特性 描述
Redelivery policies(返还策略) 返还策略允许你定义是否尝试返还的策略。此策略也可以定义尝试返还的次数、间隔等等。
Scope Camel 错误处理程序有两个错误处理范围:context(高级别的范围)和 route(低级别的范围)。context 范围允许多个路由公用同一个错误处理器,route 级别,这个错误处理器只能被一个路由使用。
Exception policies 异常策略允许你为特殊的异常定义特殊的策略
Error handling 此特性允许你决定此错误处理器是否应该处理这个错误。你可以设置让错误处理器处理这个错误,或者留给调用者处理这个错误。