The dead letter channel pattern, shown in Figure 3.3, 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.
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 onException clause).
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 3.1
summarizes the methods that you can use to set redelivery policies.
Table 3.1. Redelivery Policy Settings
Method Signature | Default | Description |
---|---|---|
backOffMultiplier(double multiplier) | 2 |
If exponential backoff is enabled, let d, m*d, m*m*d, m*m*m*d, ... |
collisionAvoidancePercent(double collisionAvoidancePercent) | 15 | If collision avoidance is enabled, let p be the collision
avoidance percent. The collision avoidance policy then tweaks the next delay by a
random amount, up to plus/minus p% of its current value. |
initialRedeliveryDelay(long initialRedeliveryDelay) | 1000 | Specifies the delay (in milliseconds) before attempting the first redelivery. |
maximumRedeliveries(int maximumRedeliveries) | 6 | Maximum number of delivery attempts. |
useCollisionAvoidance() | false | Enables collision avoidence, which adds some randomization to the backoff timings to reduce contention probability. |
useExponentialBackOff() | false | Enables exponential backoff. |
If FUSE Mediation Router attempts to redeliver a message, it automatically sets the headers described in Table 3.2 on the In message.
Table 3.2. Dead Letter Redelivery Headers
Header Name | Type | Description |
---|---|---|
org.apache.camel.RedeliveryCounter | Integer | Counts the number of unsuccessful delivery attempts. |
org.apache.camel.Redelivered | Boolean | True, if one or more redelivery attempts have been made. |
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 3.1), 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, FUSE Mediation Router 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.