Load balancing with Apache Camel
In this example we will show you how to use Apache Camel as a load balancer for your system. In computer world a load balancer is a device that acts as a reverse proxy and distributes network or application traffic across a number of servers. Load balancers are used to increase capacity (concurrent users) and reliability of applications. With the help of Camel we can make our own software load balancer in no time. Enjoy riding!
Load balancing is not a separate pattern in Enterprise Integration Patterns book (as Claus Ibsen said it would be if there was a second edition of the book) but Camel treats it just like another EIP. While Camel syntax makes load balancing looks deceivingly easy it’s still a complicated topic and you should spend some time planning a suitable strategy in design phase. Camel comes with a number of built-in load balancing policies. In this example we cover some of them that are more commonly used.
The codes for this article use Maven 3.3.9, Eclipse Mars 4.5.0 and Apache Camel 2.17.1. Please notice the load balancer API has changed a bit since version 2.15, if you are going to use earlier versions of Camel please consult the documentation. All the classes in this example source code use @Test annotated method, so you have to run them as JUnit tests and hopefully see the green bar.
1. Creating base project
Before we move on to writing actual codes lets first create a Maven project in Eclipse. First select File -> New… -> new project. Type maven and select Maven project.
In the next window check the create a simple project option and click next:
Finally add the following configuration and click finish:
Now edit the pom.xml file to look as follow:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javacodegeeks</groupId> <artifactId>camelLoadBalancer</artifactId> <version>1.0.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> <version>2.17.1</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-test</artifactId> <version>2.17.1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
2. Load balancing
We mentioned different policies that Camel support but what are policies? A policy defines how workload is distributed among different receivers (processor, consumer service…). In this article we sow examples of Random, round robin and their weighted counterparts and topic policies. At the end we show you how to make your own policy.
2.1. Random
The simplest form of load balancing policy is random. As the name implies you just define the endpoints that should handle the load and Camel decides which one to use, randomly. Here is the code that implements random policy. m1, m2 and m3 are the endpoints that handle the message. The end always receives the message but each time you run the class one of the aforementioned end points is used. This selection is totally random and you can check this randomness by adding your own assertions.
RandomLoadBalance.java
package com.jcg; import org.apache.camel.EndpointInject; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit4.CamelTestSupport; import org.junit.Test; public class RandomLoadBalance extends CamelTestSupport{ @EndpointInject(uri="mock:m1") MockEndpoint m1; @EndpointInject(uri="mock:m2") MockEndpoint m2; @EndpointInject(uri="mock:m3") MockEndpoint m3; @EndpointInject(uri="mock:end") MockEndpoint end; @Override protected RouteBuilder createRouteBuilder() throws Exception{ return new RouteBuilder(){ @Override public void configure() throws Exception { from("direct:start").loadBalance().random().to(m1,m2,m3).end().to(end); } }; } @Test public void testSending() throws Exception{ end.expectedMessageCount(1); template.sendBody("direct:start", ""); end.assertIsSatisfied(); } }
2.2. Round robin
Round robin is another simple type of load balancing. In this policy the endpoints are used in turn one by one. In the example code you see the message goes through m1, m2, m3 and again m1.
RoundRobinLoadBalance.java
package com.jcg; import javax.naming.Context; import org.apache.camel.EndpointInject; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.dataset.SimpleDataSet; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit4.CamelTestSupport; import org.junit.Test; public class RoundRobinLoadBalance extends CamelTestSupport{ @EndpointInject(uri="mock:m1") MockEndpoint m1; @EndpointInject(uri="mock:m2") MockEndpoint m2; @EndpointInject(uri="mock:m3") MockEndpoint m3; @EndpointInject(uri="mock:end") MockEndpoint end; @Override protected RouteBuilder createRouteBuilder() throws Exception{ return new RouteBuilder(){ @Override public void configure() throws Exception { from("dataset:start").loadBalance().roundRobin().to(m1,m2,m3).end().to(end); } }; } @Override protected Context createJndiContext() throws Exception{ SimpleDataSet sds = new SimpleDataSet(); sds.setSize(4); Context context = super.createJndiContext(); context.bind("start", sds); return context; } @Test public void testSending() throws Exception{ m1.expectedMessageCount(2); m2.expectedMessageCount(1); m3.expectedMessageCount(1); end.expectedMessageCount(4); template.sendBody("dataset:start", ""); m1.assertIsSatisfied(); m2.assertIsSatisfied(); m3.assertIsSatisfied(); end.assertIsSatisfied(); } }
2.3. Weighted round robin
In real world it rarely happens that you have some identical machines serving your requests. So it makes sense to have one machine that is probably more powerful does more job than others. Weighted round robin and weighted random are more sophisticated counterparts of round robin and random policies respectively. With weighted round robin (or random) you can control load balancing in a fine-grained manner. Here we give an example of weighted round robin. Weighted random is identical.
The weight method has two arguments. First is a boolean that defines if policy is round robin (true) or random (false). The second argument is a String that defines distribution ratio of the corresponding endpoints. Here “2,1” means m1 receives twice as much traffic as m2 receives. In earlier version of Camel you had to use a List of integers.
WeightedRoundRobinLoadBalance.java
package com.jcg; import javax.naming.Context; import org.apache.camel.EndpointInject; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.dataset.SimpleDataSet; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit4.CamelTestSupport; import org.junit.Test; public class WeightedRoundRobinLoadBalance extends CamelTestSupport{ @EndpointInject(uri="mock:m1") MockEndpoint m1; @EndpointInject(uri="mock:m2") MockEndpoint m2; @EndpointInject(uri="mock:end") MockEndpoint end; @Override protected RouteBuilder createRouteBuilder() throws Exception{ return new RouteBuilder(){ @Override public void configure() throws Exception { // first argument of weighted method is a boolean defines if the policy is // Round robin (true) or Random (false) from("dataset:start").loadBalance().weighted(true,"2,1").to(m1,m2).end().to(end); } }; } @Override protected Context createJndiContext() throws Exception{ SimpleDataSet sds = new SimpleDataSet(); sds.setSize(6); Context context = super.createJndiContext(); context.bind("start", sds); return context; } @Test public void testSending() throws Exception{ m1.expectedMessageCount(4); m2.expectedMessageCount(2); end.expectedMessageCount(6); template.sendBody("dataset:start", ""); m1.assertIsSatisfied(); m2.assertIsSatisfied(); end.assertIsSatisfied(); } }
2.4. Topic
Topic is fundamentally different from other policies because all the end points receive the message. In our example code m1, m2 and m3 all process 5 messages and the end endpoint receives 5 messages too. Topic can be useful in guarding against endpoint failures.
TopicLoadBalance.java
package com.jcg; import javax.naming.Context; import org.apache.camel.EndpointInject; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.dataset.SimpleDataSet; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit4.CamelTestSupport; import org.junit.Test; public class TopicLoadBalance extends CamelTestSupport{ @EndpointInject(uri="mock:m1") MockEndpoint m1; @EndpointInject(uri="mock:m2") MockEndpoint m2; @EndpointInject(uri="mock:m3") MockEndpoint m3; @EndpointInject(uri="mock:end") MockEndpoint end; @Override protected RouteBuilder createRouteBuilder() throws Exception{ return new RouteBuilder(){ @Override public void configure() throws Exception { from("dataset:start").loadBalance().topic().to(m1,m2,m3).end().to(end); } }; } @Override protected Context createJndiContext() throws Exception{ SimpleDataSet sds = new SimpleDataSet(); sds.setSize(5); Context context = super.createJndiContext(); context.bind("start", sds); return context; } @Test public void testSending() throws Exception{ m1.expectedMessageCount(5); m2.expectedMessageCount(5); m3.expectedMessageCount(5); end.expectedMessageCount(5); template.sendBody("dataset:start", ""); m1.assertIsSatisfied(); m2.assertIsSatisfied(); m3.assertIsSatisfied(); end.assertIsSatisfied(); } }
2.5. Custom load balancer
Camel offers many useful policies but there are always times when none of them fulfill your needs. In such situations you have to define your own load balancing strategy and still Camel doesn’t leave you alone. Custom load balancer lets you define your own policy easily. In the example code we define a load balancer that checks the message header “sessionID” field. If it is even it sends it to m1, if odd to m2. Of course this is an unrealistic example but we deliberately made it simple to be able to focus on load balancing implementation free of business logic clutter. First we make a class that extends LoadBalancerSupport class and override process method. Then we pass an instance of this class to loadBalance method.
SessionChecker.java
package com.jcg; import org.apache.camel.AsyncCallback; import org.apache.camel.Exchange; import org.apache.camel.processor.loadbalancer.LoadBalancerSupport; public class SessionChecker extends LoadBalancerSupport{ @Override public boolean process(Exchange exchange, AsyncCallback callback) { int id = exchange.getIn().getHeader("sessionID", Integer.class); try{ if(id%2 == 0){ getProcessors().get(0).process(exchange); } else{ getProcessors().get(1).process(exchange); } }catch(Exception e){ e.printStackTrace(); } callback.done(true); return true; } }
CustomLoadBalance.java
package com.jcg; import java.util.HashMap; import java.util.Map; import javax.naming.Context; import org.apache.camel.EndpointInject; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.dataset.SimpleDataSet; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit4.CamelTestSupport; import org.junit.Test; public class CustomLoadBalance extends CamelTestSupport{ @EndpointInject(uri="mock:m1") MockEndpoint m1; @EndpointInject(uri="mock:m2") MockEndpoint m2; @EndpointInject(uri="mock:end") MockEndpoint end; @Override protected RouteBuilder createRouteBuilder() throws Exception{ return new RouteBuilder(){ @Override public void configure() throws Exception { from("dataset:start").loadBalance(new SessionChecker()).to(m1,m2).end().to(end); } }; } @Override protected Context createJndiContext() throws Exception{ SimpleDataSet sds = new SimpleDataSet(); Map<String, Object> headers = new HashMap<>(); headers.put("sessionID", 1); sds.setDefaultHeaders(headers); sds.setSize(2); Context context = super.createJndiContext(); context.bind("start", sds); return context; } @Test public void testSending() throws Exception{ m1.expectedMessageCount(0); m2.expectedMessageCount(2); end.expectedMessageCount(2); template.sendBody("dataset:start", ""); m1.assertIsSatisfied(); m2.assertIsSatisfied(); end.assertIsSatisfied(); } }
3. Conclusion
Still there’s more into Camel load balancing. We didn’t cover policies such as Failover, Sticky and Circuit breaker. In fact Camel goes beyond this. You can use load balancing in many more situations such as an HTTP proxy between client and server.
4. Download the Eclipse project
This was an example of different Camel load balancing policies.
You can download the full source code of this example here: Camel load balancer
i need java code for weighted round robin load balancing for cloud computing.
i am using cloud analyst tool.