5.3. Dead Letter Channel

来源:互联网 发布:上海市软件行业协会 编辑:程序博客网 时间:2024/06/05 07:06

Overview

The dead letter channel pattern, shown in Figure 5.3, “Dead Letter Channel Pattern”, describes the actions to take when the messaging system fails to deliver a message to the intended recipient. This includes such features as retrying delivery and, if delivery ultimately fails, sending the message to a dead letter channel, which archives the undelivered messages.

Figure 5.3. Dead Letter Channel Pattern 
这里写图片描述

Creating a dead letter channel in Java DSL

The following example shows how to create a dead letter channel using Java DSL:

errorHandler(deadLetterChannel("seda:errors"));from("seda:a").to("seda:b");

Where the errorHandler() method is a Java DSL interceptor, which implies that all of the routes defined in the current route builder are affected by this setting. The deadLetterChannel() method is a Java DSL command that creates a new dead letter channel with the specified destination endpoint, seda:errors.

The errorHandler() interceptor provides a catch-all mechanism for handling all error types. If you want to apply a more fine-grained approach to exception handling, you can use the onException clauses instead(see the section called “onException clause”).

XML DSL example

You can define a dead letter channel in the XML DSL, as follows:

 <route errorHandlerRef="myDeadLetterErrorHandler">    ... </route> <bean id="myDeadLetterErrorHandler" class="org.apache.camel.builder.DeadLetterChannelBuilder">     <property name="deadLetterUri" value="jms:queue:dead"/>     <property name="redeliveryPolicy" ref="myRedeliveryPolicyConfig"/> </bean> <bean id="myRedeliveryPolicyConfig" class="org.apache.camel.processor.RedeliveryPolicy">     <property name="maximumRedeliveries" value="3"/>     <property name="redeliveryDelay" value="5000"/> </bean>

Redelivery policy

Normally, you do not send a message straight to the dead letter channel, if a delivery attempt fails. Instead, you re-attempt delivery up to some maximum limit, and after all redelivery attempts fail you would send the message to the dead letter channel. To customize message redelivery, you can configure the dead letter channel to have a redelivery policy. For example, to specify a maximum of two redelivery attempts, and to apply an exponential backoff algorithm to the time delay between delivery attempts, you can configure the dead letter channel as follows:

errorHandler(deadLetterChannel("seda:errors").maximumRedeliveries(2).useExponentialBackOff());from("seda:a").to("seda:b");

Where you set the redelivery options on the dead letter channel by invoking the relevant methods in a chain (each method in the chain returns a reference to the current RedeliveryPolicy object). Table 5.1, “Redelivery Policy Settings” summarizes the methods that you can use to set redelivery policies.

Table 5.1. Redelivery Policy Settings 
这里写图片描述 
这里写图片描述 
这里写图片描述

Redelivery headers

If Apache Camel attempts to redeliver a message, it automatically sets the headers described in Table 5.2, “Dead Letter Redelivery Headers” on the In message.

Table 5.2. Dead Letter Redelivery Headers 
这里写图片描述

Redelivery exchange properties

If Apache Camel attempts to redeliver a message, it automatically sets the exchange properties described in Table 5.3, “Redelivery Exchange Properties”.

Table 5.3. Redelivery Exchange Properties 
这里写图片描述

Using the original message

Available as of Apache Camel 2.0 Because an exchange object is subject to modification as it passes through the route, the exchange that is current when an exception is raised is not necessarily the copy that you would want to store in the dead letter channel. In many cases, it is preferable to log the message that arrived at the start of the route, before it was subject to any kind of transformation by the route. For example, consider the following route:

from("jms:queue:order:input")       .to("bean:validateOrder");       .to("bean:transformOrder")       .to("bean:handleOrder");

The preceding route listen for incoming JMS messages and then processes the messages using the sequence of beans: validateOrder, transformOrder, and handleOrder. But when an error occurs, we do not know in which state the message is in. Did the error happen before the transformOrder bean or after? We can ensure that the original message from jms:queue:order:input is logged to the dead letter channel by enabling the useOriginalMessage option as follows:

// will use original bodyerrorHandler(deadLetterChannel("jms:queue:dead")       .useOriginalMessage().maximumRedeliveries(5).redeliveryDelay(5000);

Redeliver delay pattern

Available as of Apache Camel 2.0 The delayPattern option is used to specify delays for particular ranges of the redelivery count. The delay pattern has the following syntax: limit1:delay1;limit2:delay2;limit3:delay3;…, where each delayN is applied to redeliveries in the range limitN <= redeliveryCount < limitN+1 
For example, consider the pattern, 5:1000;10:5000;20:20000, which defines three groups and results in the following redelivery delays:

Attempt number 1..4 = 0 milliseconds (as the first group starts with 5).Attempt number 5..9 = 1000 milliseconds (the first group).Attempt number 10..19 = 5000 milliseconds (the second group).Attempt number 20.. = 20000 milliseconds (the last group). 

You can start a group with limit 1 to define a starting delay. For example, 1:1000;5:5000 results in the following redelivery delays:

Attempt number 1..4 = 1000 millis (the first group)Attempt number 5.. = 5000 millis (the last group) 

There is no requirement that the next delay should be higher than the previous and you can use any delay value you like. For example, the delay pattern, 1:5000;3:1000, starts with a 5 second delay and then reduces the delay to 1 second.

Which endpoint failed?

When Apache Camel routes messages, it updates an Exchange property that contains the last endpoint the Exchange was sent to. Hence, you can obtain the URI for the current exchange’s most recent destination using the following code:

// JavaString lastEndpointUri = exchange.getProperty(Exchange.TO_ENDPOINT, String.class);

Where Exchange.TO_ENDPOINT is a string constant equal to CamelToEndpoint. This property is updated whenever Camel sends a message to any endpoint.

If an error occurs during routing and the exchange is moved into the dead letter queue, Apache Camel will additionally set a property named CamelFailureEndpoint, which identifies the last destination the exchange was sent to before the error occcured. Hence, you can access the failure endpoint from within a dead letter queue using the following code:

// JavaString failedEndpointUri = exchange.getProperty(Exchange.FAILURE_ENDPOINT, String.class);

Where Exchange.FAILURE_ENDPOINT is a string constant equal to CamelFailureEndpoint.

Note 
These properties remain set in the current exchange, even if the 
failure occurs after the given destination endpoint has finished 
processing. For example, consider the following route:


from("activemq:queue:foo") 
.to("http://someserver/somepath") 
.beanRef("foo");

Now suppose that a failure happens in the foo bean. In this case the 
Exchange.TO_ENDPOINT property and the Exchange.FAILURE_ENDPOINT 
property still contain the value, http://someserver/somepath.

onRedelivery processor

When a dead letter channel is performing redeliveries, it is possible to configure a Processor that is executed just before every redelivery attempt. This can be used for situations where you need to alter the message before it is redelivered. 
For example, the following dead letter channel is configured to call the MyRedeliverProcessor before redelivering exchanges:

// we configure our Dead Letter Channel to invoke// MyRedeliveryProcessor before a redelivery is// attempted. This allows us to alter the message beforeerrorHandler(deadLetterChannel("mock:error").maximumRedeliveries(5)        .onRedelivery(new MyRedeliverProcessor())        // setting delay to zero is just to make unit teting faster        .redeliveryDelay(0L));

Where the MyRedeliveryProcessor process is implemented as follows:

// This is our processor that is executed before every redelivery attempt// here we can do what we want in the java code, such as altering the messagepublic class MyRedeliverProcessor implements Processor {    public void process(Exchange exchange) throws Exception {        // the message is being redelivered so we can alter it        // we just append the redelivery counter to the body        // you can of course do all kind of stuff instead        String body = exchange.getIn().getBody(String.class);        int count = exchange.getIn().getHeader(Exchange.REDELIVERY_COUNTER, Integer.class);        exchange.getIn().setBody(body + count);        // the maximum redelivery was set to 5        int max = exchange.getIn().getHeader(Exchange.REDELIVERY_MAX_COUNTER, Integer.class);        assertEquals(5, max);    }}

Control redelivery during shutdown or stopping

If you stop a route or initiate graceful shutdown, the default behavior of the error handler is to continue attempting redelivery. Because this is typically not the desired behavior, you have the option of disabling redelivery during shutdown or stopping, by setting the allowRedeliveryWhileStopping option to false, as shown in the following example:

errorHandler(deadLetterChannel("jms:queue:dead")    .allowRedeliveryWhileStopping(false)    .maximumRedeliveries(20)    .redeliveryDelay(1000)    .retryAttemptedLogLevel(LoggingLevel.INFO));

Note 
The allowRedeliveryWhileStopping option is true by default, for 
backwards compatibility reasons. During aggressive shutdown, however, 
redelivery is always suppressed, irrespective of this option setting 
(for example, after graceful shutdown has timed out).

onException clause

Instead of using the errorHandler() interceptor in your route builder, you can define a series of onException() clauses that define different redelivery policies and different dead letter channels for various exception types. For example, to define distinct behavior for each of the NullPointerException, IOException, and Exception types, you can define the following rules in your route builder using Java DSL:

onException(NullPointerException.class)    .maximumRedeliveries(1)    .setHeader("messageInfo", "Oh dear! An NPE.")    .to("mock:npe_error");onException(IOException.class)    .initialRedeliveryDelay(5000L)    .maximumRedeliveries(3)    .backOffMultiplier(1.0)    .useExponentialBackOff()    .setHeader("messageInfo", "Oh dear! Some kind of I/O exception.")    .to("mock:io_error");onException(Exception.class)    .initialRedeliveryDelay(1000L)    .maximumRedeliveries(2)    .setHeader("messageInfo", "Oh dear! An exception.")    .to("mock:error");from("seda:a").to("seda:b");

Where the redelivery options are specified by chaining the redelivery policy methods (as listed in Table 5.1, “Redelivery Policy Settings”), and you specify the dead letter channel’s endpoint using the to() DSL command. You can also call other Java DSL commands in the onException() clauses. For example, the preceding example calls setHeader() to record some error details in a message header named, messageInfo.

In this example, the NullPointerException and the IOException exception types are configured specially. All other exception types are handled by the generic Exception exception interceptor. By default, Apache Camel applies the exception interceptor that most closely matches the thrown exception. If it fails to find an exact match, it tries to match the closest base type, and so on. Finally, if no other interceptor matches, the interceptor for the Exception type matches all remaining exceptions.

0 0
原创粉丝点击