Apache load balancer for Tomcat | Day in my life

October 12, 2013

Apache load balancer for Tomcat

This is a follow-up post to my earlier one apache and tomcat. You could read more about the Tomcat clustering here which describes a more detailed view of load balancing and persistent sessions with-in-memory session replication and so on. But in this post, I will discuss a basic load balancing solution with sample working solution.

The mod_proxy module can also be used for load balancing but will not be discussed here, the mod_jk module supports load balancing with seamless sessions, it uses a simple round-robin algorithm. Each Tomcat worker is weighted in the worker.properties file which specifies how the request load is distributed between workers.

A seamless session is also known as session affinity or a sticky session. When a request is made any of the Tomcat instances is used, but any subsequent request will be routed to the same Tomcat instance to keep the same user session.

The following steps are required to set up load balancing in Tomcat

In this test setup, all the Tomcat instances will be running on the same server (localhost).

Now in each Tomcat instance, we must set a different AJP Connector port number (<tomcat_install>/conf/server.xml)

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Note: on the other Tomcat instances use ports 8010 and 8011

To avoid startup/shutdown port conflicts we must change each Tomcats worker server port (<tomcat_install>/conf/server.xml) server port

<Server port="8005" shutdown="SHUTDOWN" debug="0">

Note: on the other Tomcat instances use ports 8006 and 8007

Because all the Tomcat instances will be running in conjunction with the load-balancer worker, it’s possible that someone could directly access any of the available workers via the default HTTP Connector, bypassing the load-balancer path. To avoid this comment out the HTTP Connector configuration of all the Tomcat instances (<tomcat_install>/conf/server.xml).

<!-- Define 
<Connector port="8080"
                protocol="HTTP/1.1"
                maxThreads="150"
                connectionTimeout="20000"
                redirectPort="8443"
/>
-->

An important step for load balancing is specifying the jvmRoute. The jvmRoute is an attribute of Engine directive that acts as an identifier for that particular Tomcat worker. The attribute must be unique across all Tomcat instances, this unique ID is used in the worker.properties file for identifying each Tomcat worker (<tomcat_install>/conf/server.xml).

<Engine name="Standalone" defaultHost="localhost" jvmRoute="tomcatA" >

Note: the other Tomcat instances would be tomcatB and tomcatC

You will also need to comment out the Catalina Engine directive in <tomcat_install>/conf/server.xml.

<!-- Define 
<Engine name="Catalina" defaultHost="localhost" > 
-->

Note: the other Tomcat instances would be tomcatB and tomcatC

In Apache’s httpd.conf file you need to add some load balancing directives, also make sure you have the module mod_jk loaded httpd.conf directives

JkWorkersFile   conf/worker.properties
JkLogFile       logs/mod_jk.log
JkShmFile       logs/mod_jk.shm
JkMount         /examples/jsp/*  loadbal1
JkMount         /jkstatus/  stat1
JkLogLevel      debug

The last thing is to create the worker.properties file, as discussed in post apache and tomcat and place it and configure it to be picked up by JkWorkersFile attribute in httpd.conf

worker.list = loadbal1,stat1

worker.tomcatA.type = ajp13
worker.tomcatA.host = localhost
worker.tomcatA.port = 8009
worker.tomcatA.lbfactor = 10

worker.tomcatB.type = ajp13
worker.tomcatB.host = localhost
worker.tomcatB.port = 8010
worker.tomcatB.lbfactor = 10

worker.tomcatC.type = ajp13
worker.tomcatC.host = localhost
worker.tomcatC.port = 8011
worker.tomcatC.lbfactor = 10

worker.loadbal1.type = lb
worker.loadbal1.sticky_session = 1
worker.loadbal1.balance_workers = tomcatA, tomcatB, tomcatC

worker.stat1.type = status

To test the load balancer and sticky sessions use the below JSP page (index.jsp), just place it in the webapps/examples/jsp directory.

<%@ page language="java" %>
<%@ page import = "java.io.*,java.util.*, javax.servlet.*"%>
<html>
<body>
    <h1><font color="red">Index Page by tomcatA</font></h1>
    <table align="centre" border="1">
        <tr>
            <td>Session ID</td>
            <td><%= session.getId() %></td>
        </tr>
        <tr>
            <td>Created On</td>
            <td><% out.print(new Date(session.getCreationTime()).toString()); %></td>
        </tr>
    </table>
</body>
</html>

Note: Modify h1 tag to reflect the other Tomcat instances i.e. tomcatB and tomcatC when its copied

Use the below URL’s for testing the setup. Don’t forget to play around with the lbfactor on each Tomcat instance to see what effect it has.

http://localhost/examples/jsp/index.jsp
http://localhost/jkstatus/

Update on 30/09/2018: Tested the above setup with Tomcat 9.x and it still works!

© Nataraj Basappa 2018