Routing Slip and Loading Balancer EIPs in Camel

This article is based on Camel in Action, published on December 2010. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) ebooks and pbooks. MEAPs are sold exclusively through Manning.com. All print book purchases include an ebook free of charge. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information.

also read:

Routing Slip and Loading Balancer EIPs

Introduction

Today’s businesses are not run on single monolithic systems, and most businesses have a full range of disparate systems. There is an ever increasing demand for those systems to integrate and to integrate with external business partners and government systems.

Let’s face it, integration is a hard problem. To help deal with the complexity of integration problems, the Enterprise Integration Patterns (EIP) have become the standard way to describe, document, and implement complex integration problems. Gregor Hohpe and Bobby Woolf’s book Enterprise Integration Patterns1 has become the bible in the integration space and an essential reading for any integration professional.

The EIP book distils 64 patterns in approx 700 pages; Camel implements nearly all those patterns in addition to eight patterns. This article is devoted to two of the most powerful and feature rich patterns, Routing Slip and Loading Balancer.

Routing Slip EIP

There are times when you need to route messages in dynamic fashion. For example, you may have an architecture processing incoming messages that have to undergo a sequence of processing steps and business rule validations. Since the nature of the steps and validations varies widely, we implement each type of the step as a separate filter. The filter acts as a dynamic model to apply the business rule(s) and validations when applicable.

This architecture could be implemented using the pipes and filters together with the Filter EIP. However, as often with the EIP patterns, there is a nicer way known as the Routing Slip EIP pattern. Routing Slip acts as a dynamic route that dictates the next step a message should undergo. Figure 1 shows this principle.

The Camel Routing Slip EIP requires using a preexisting header as the attached slip; this means you must prepare and attach the header beforehand.

We start with a simple example that shows how to use the Routing Slip EIP, which uses the sequence outlined in figure 1. In Java DSL the route is as simple as follows:

from("direct:start").routingSlip("mySlip");
And, in Spring XML, it's easy as well:
<route>
	<from uri="direct:start"/>
	<routingSlip headerName="mySlip"/>
</route>

This example assumes the incoming message contains the attached slip in the header with the key “mySlip”. The following test method shows how you should fill out the key.

public void testRoutingSlip() throws Exception {
	getMockEndpoint("mock:a").expectedMessageCount(1);
	getMockEndpoint("mock:b").expectedMessageCount(0);
	getMockEndpoint("mock:c").expectedMessageCount(1);
	template.sendBodyAndHeader("direct:start", "Hello World",
	"mySlip", "mock:a,mock:c");
	assertMockEndpointsSatisfied();
}

As you can see, the value of the key is simply the endpoint URIs separated by a comma. Comma is the default delimiter; however, the routing slip supports using custom delimiters. For example, to use a semicolon you do:

from("direct:start").routingSlip("mySpli", ";");

And, in Spring XML:

<routingSlip headerName="mySlip" uriDelimiter=";"/>

NOTE

The Camel Routing Slip may be improved in the future to use an Expression to compute the slip. Currently, it requires the slip to preexist as a header. The Camel team will also implement more EIP patterns as annotations in future releases of Camel, which means a @RoutingSlip annotation is most likely to appear.

The example we just covered expected a preexisting header to contain the routing slip. What if the message does not contain such a header? Well, in those situations, you have to compute the header beforehand in any way you like. In the next example we show how to compute the header using a bean.

Using a bean to compute the routing slip header

To keep things simple, we have kept the logic to compute a header that either contains two or three steps:

public class ComputeSlip {
	public String compute(String body) {
		String answer = "mock:a";
		if (body.contains("Cool")) {
			answer += ",mock:b";
		}
		answer += ",mock:c";
		return answer;
	}
}

All we know how to do is to leverage this bean to compute the routing slip header prior to the Routing Slip EIP. In Java DSL, we can use the method call expression to invoke the bean and set the header as highlighted below:

from("direct:start")
.setHeader("mySlip").method(ComputeSlip.class)
.routingSlip("mySlip");

And, in Spring XML, you can do it as follows:

<route>
<from uri="direct:start"/>
<setHeader headerName="mySlip">
<method beanType="camelinaction.ComputeSlip"/>
</setHeader>
<routingSlip headerName="mySlip"/>
</route>

The source code for Camel in Action contains the examples we have covered in the chapter8/routingslip directory, which you can try using the following maven goals:

mvn test -Dtest=RoutingSlipSimpleTest
mvn test -Dtest=SpringRoutingSlipSimpleTest
mvn test -Dtest=RoutingSlipTest
mvn test -Dtest=SpringRoutingSlipTest

You have now seen the Routing Slip EIP in action which concludes the third of the four patterns from table 8.1 we will visit in this chapter.

Load Balancer was not distilled in the EIP book, which necessarily is not a bad thing. If the authors of the EIP book wrote a second edition, they most likely would’ve added a pattern about load balancing. In the next section you will learn about Camel’s built-in Load Balancer EIP, which makes it easy for you to leverage in cases where a load balancing solution is not in place.

Load Balancer EIP

You may already be familiar with the load balancing2 concept in computing. Load balancing is a technique to distribute work load across computers or other resources, in order to optimize utilization, improve throughput, minimize response time and avoid overload. This service can be provided either in the form of a hardware device or as a piece of software. Such a piece of software is provided in Camel as the Load Balancer EIP pattern.

In this section we will learn about the Load Balancer EIP pattern by walking through an example. Then, we’ll see the various types of load balancer Camel offers out of the box. Then, we’ll focus on the failover type. You can also build your own load balancer, which we cover last.

Introducing Camel Load Balancer EIP

The Camel Load Balancer EIP is a Processor that implements the interface org.apache.camel.processor.loadbalancer.LoadBalancer. The LoadBalancer offers methods to add and remove Processors that should participate. By using Processor instead of Endpoints, the load balancer is capable of balancing anything that you can define in your Camel routes. You can draw a parallel to the Content-Based Router EIP pattern, which is capable of letting messages choose different route paths.

However, it’s most often you would use the Load Balancer EIP to balance across a number of external remote services. Such an example is illustrated in figure 2, where a Camel application needs to load-balance across two services.

When using the Load Balancer EIP, you have to select a strategy. A common and easy-to-understand strategy would be to let it take turns among the services, which is known as the round robin strategy. We will take a look at all the strategies Camel provides out of the box.

We will now illustrate how to use the Load Balancer with the round robin strategy based on a small test example. First, we show the Java DSL with the load balancer highlighted:

from("direct:start")
.loadBalance().roundRobin()
.to("seda:a").to("seda:b")
.end();
from("seda:a")
.log("A received: ${body}")
.to("mock:a");
from("seda:b")
.log("B received: ${body}")
.to("mock:b");

And, the equivalent route in Spring XML is as follows:

<route>
<from uri="direct:start"/>
<loadBalance>
<roundRobin/>
<to uri="seda:a"/>
<to uri="seda:b"/>
</loadBalance>
</route>
<route>
<from uri="seda:a"/>
<log message="A received: ${body}"/>
<to uri="mock:a"/>
</route>
<route>
<from uri="seda:b"/>
<log message="B received: ${body}"/>
<to uri="mock:b"/>
</route>

The routes will load balance across the two nodes which is sending the message to the external services:

	.to("seda:a").to("seda:b")

Suppose we start sending messages to the route. What would happen is that the first message would be sent to the “seda:a” endpoint and the next would go to “seda:b”. Then, the third message would start all over and be send to “seda:a”, and so forth.

The source code for Camel in Action contains this example in the chapter8/loadbalancer directory, which you can try using the following maven goals:

mvn test -Dtest=LoadBalancerTest
mvn test -Dtest=SpringLoadBalancerTest

If you run the example, the console would output something like this:

[Camel Thread 0 - seda://a] INFO route2 - A received: Hello
[Camel Thread 1 - seda://b] INFO route3 - B received: Camel rocks
[Camel Thread 0 - seda://a] INFO route2 - A received: Cool
[Camel Thread 1 - seda://b] INFO route3 - B received: Bye

In the next section we will review the various load balancer strategies you can use with the Load Balancer EIP.

Load balancer strategies

A load balancer strategy is used for dictating which Processor should process an incoming message and it’s totally up to each strategy how they chose the Processor. Camel provides six strategies, listed in table 1.

Table 1 Load balancer strategies provided by Camel

The first four strategies are easy to set up and use in Camel. For example, using the random strategy is just a matter of specifying in Java DSL:

from("direct:start")
.loadBalance().random()
.to("seda:a").to("seda:b")
.end();

And, in Spring XML:

<route>
<from uri="direct:start"/>
<loadBalance>
<random/>
<to uri="seda:a"/>
<to uri="seda:b"/>
</loadBalance>
</route>

However, the sticky strategy requires that you provide a correlation expression, which is used to calculate a hashed value to indicate which processor to use. Suppose your messages contain a header indicating different levels. Using the sticky strategy you can have messages with the same level choose the same processor over and over again.

In Java DSL, you would provide the expression as highlighted:

from("direct:start")
.loadBalance().sticky(header("type"))
.to("seda:a").to("seda:b")
.end();

And, in Spring XML:

<route>
<from uri="direct:start"/>
<loadBalance>
<sticky>
<correlationExpression>
<header>type</header>
</correlationExpression>
</sticky>
<to uri="seda:a"/>
<to uri="seda:b"/>
</loadBalance>
</route>

The source code for Camel in Action contains examples for the strategies listed in table 1 in the chapter8/loadbalancer directory. For example, to try random, sticky, or topic you can use the following maven goals:

mvn test -Dtest=RandomLoadBalancerTest
mvn test -Dtest=SpringRandomLoadBalancerTest
mvn test -Dtest=StickyLoadBalancerTest
mvn test -Dtest=SpringStickyLoadBalancerTest
mvn test -Dtest=TopicLoadBalancerTest
mvn test -Dtest=SpringTopicLoadBalancerTest

The failover strategy is a more elaborate strategy, which we cover now.

Using the failover load balancer

Load balancing is often used to implement failover—the continuation of a service after a failure. The Camel failover load balancer detects the failure when an exception occurs and reacts by letting the next processor take over processing the message.

Given the route snippet below, the failover will always start by sending the messages to the first processor (“direct:a”) and only in case of a failure it will let the next (“direct:b”) take over, and so forth.

from("direct:start")
.loadBalance().failover()
.to("direct:a").to("direct:b")
.end();

And, the equivalent snippet in Spring XML is as follows:

<route>
<from uri="direct:start"/>
<loadBalance>
<failover/>
<to uri="direct:a"/>
<to uri="direct:b"/>
</loadBalance>
</route>

The source code for Camel in Action contains this example in the chapter8/loadbalancer directory, which you can try using the following maven goals:

mvn test -Dtest=FailoverLoadBalancerTest
mvn test -Dtest=SpringFailoverLoadBalancerTest

If you run the example, it’s constructed to send in four messages, the second of which will failover to be processed by the “direct:b” processor. The other three messages will be processed successfully by “direct:a”.

In this example, the failover load balancer will react to any kind of exception. However, you can provide it with a number of exceptions that make it reacts.

Suppose we only want to failover if an IOException is thrown (which indicates communication errors with external services, such as no connection). This is very easy to configure, as shown in Java DSL:

from("direct:start")
.loadBalance().failover(IOException.class)
.to("direct:a").to("direct:b")
.end();

And, in Spring XML:

<route>
<from uri="direct:start"/>
<loadBalance>
<failover>
<exception>java.io.IOException</exception>
</failover>
<to uri="direct:a"/>
<to uri="direct:b"/>
</loadBalance>
</route>

In this example, we only specified one exception; however, you can specify multiple exceptions.

You may have noticed in the failover examples that the load balancer works by always choosing the first processor and the failover to subsequent processors. In other words, the first processor is the master and the others are slaves.

The failover load balancer offers a strategy that combines round robin with failure support.

USING FAILOVER WITH ROUND ROBIN

The Camel failover load balancer in round robin mode allows you to use the best of two worlds. This allows you to distribute the load evenly between the services and have automatic failover as well.

In this scenario, you have three configuration options on the load balancer that dictate how it operates at runtime, as listed in table 2.

Table 2 Failover load balancer configuration options

To better understand the options from table 2 and how the round robin mode works, we start with a fairly simple example.

In Java DSL, you have to configure the failover with all of the options, as highlighted below:

from("direct:start")
.loadBalance().failover(1, false, true)
.to("direct:a").to("direct:b")
.end();

In this example, we have set the maximumFailoverAttempts to 1, which means we will at most try to failover once. That means we will try at most two times, one for the initial request and then the failover attempt. If both fail, Camel propagates the exception back to the caller. In this example, we have disabled the Camel error handler. We will discuss this option in more detail in the next example. And, the last parameter indicates that we use the round robin mode.

In Spring XML, you configure the options as attributes on the <failover/> EIP:

<route>
<from uri="direct:start"/>
<loadBalance>
<failover roundRobin="true" maximumFailoverAttempts="1"/>
<to uri="direct:a"/>
<to uri="direct:b"/>
</loadBalance>
</route>

The source code for Camel in Action contains this example in the chapter8/loadbalancer directory, which you can try using the following maven goals:

mvn test -Dtest=FailoverLoadBalancerTest
mvn test -Dtest=SpringFailoverLoadBalancerTest

If you are curious about the inheritErrorHandler configuration option, please look at the following examples in the source code for Camel in Action:

mvn test -Dtest=FailoverInheritErrorHandlerLoadBalancerTest
mvn test -Dtest=SpringFailoverInheritErrorHandlerLoadBalancerTest

This concludes our tour of the failover load balancer. The next section teaches us how to implement and use our own custom strategy, which you may want to use when you need a special balancing logic.

Using a custom load balancer

When implementing a custom load balancer, you would often extend the LoadBalancerSupport class, which provides a good starting point. Listing 1 shows how we have implemented the custom load balancer.

Listing 1 Custom load balancer

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.processor.loadbalancer.LoadBalancerSupport;
public class MyCustomLoadBalancer extends LoadBalancerSupport {
	public void process(Exchange exchange) throws Exception {
	Processor target = chooseProcessor(exchange);
	target.process(exchange);
}
protected Processor chooseProcessor(Exchange exchange) {
	String type = exchange.getIn().getHeader("type", String.class);
	if ("gold".equals(type)) {
		return getProcessors().get(0);
		} else {
		return getProcessors().get(1);
	}
}
}

As you can see, it doesn’t take much code. In the process() method we invoke the chooseProcessor() method, which essentially is the strategy that picks the processor to process the message. In this example, it will pick the first processor, if the message is gold type, and the second processor, if not.

In Java DSL, you use a custom load balancer as highlighted:

from("direct:start")
.loadBalance(new MyCustomLoadBalancer())
.to("seda:a").to("seda:b")
.end();

And in Spring XML, you need to declare a Spring <bean/> tag:

<bean id="myCustom" class="camelinaction.MyCustomLoadBalancer"/>

Which you then refer to from the <loadBalance> EIP:

<route>
<from uri="direct:start"/>
<loadBalance ref="myCustom">
<to uri="seda:a"/>
<to uri="seda:b"/>
</loadBalance>
</route>

The source code for Camel in Action contains this example in the chapter8/loadbalancer directory, which you can try using the following maven goals:

mvn test -Dtest=CustomLoadBalancerTest
mvn test -Dtest=SpringCustomLoadBalancerTest

We have now covered all about the Load Balancer EIP in Camel. You now know that, when you are in need of a load balancing solution, you can maybe settle with a build in Load Balancer EIP in Camel. In fact, you can even build your own custom strategy when the out-of-the-box strategies don’t meet your requirements. For example, you could build a strategy that acquires load stats from services and picks the service with the lowest load.

Summary

Thanks to the arrival of the EIP book on the scene, we now have a common vocabulary and concepts when we architect applications to tackle today’s integration challenges. This article reviews two of the most complex and sophisticated patterns in great details.

We covered Routing Slip EIP and Load Balancer EIP, the latter of which was not portrayed in the EIP book. We cover how it works and the six different strategies Camel offers out of the box.

You can visit the Camel website4 for a list of the EIP patterns implemented in Camel and for additional information about those patterns.

Comments

comments

About Krishna Srinivasan

He is Founder and Chief Editor of JavaBeat. He has more than 8+ years of experience on developing Web applications. He writes about Spring, DOJO, JSF, Hibernate and many other emerging technologies in this blog.

Comments

  1. Ken Ohaeri says:

    Hi Krishna,
    Do you still do work in Camel Routes? If you do please let me know. I have a technical scenario I will like to discuss with you. It involves load-balancing in one aspect and fail-over in another but only one component needs to have fail-over the other container will just perform round-robin, weighted-round-robin and sticky load-balancing.
    Please let me know if you will be interested in giving me some pointers on this situation.
    Thanks!

Speak Your Mind

*