Tomcat is by far the most popular open source Java servlet container that can easily be scaled to ensure high availability. In this post I will show how easy it is to distribute and scale java web applications using tomcat clusters. In this example we will also use Apache web server to load balance the cluster.
Below is a simple setup of a cluster with two Tomcat 7 nodes with Apache web server 2.2 as a load balancer.
Clustering
Load balanced clustering on tomcat is done in four simple steps:
1- Uniquely naming each node
2- Configure clustering in server.xml
3- Configure the session persistence in context.xml (Optional)
4- Enable distribution in the Java web application
The following configuration is done on each tomcat instance either running on the same machine or on separate virtual or physical servers. If multiple instances are running on the same machine then it should be ensured that each instance is listening on different ports.
Naming the tomcat nodes
The server node can be named by adding the jvmRoute element to the tomcat Engine as shown below. This node name is appended to the session id.
<Engine name="Catalina" jvmRoute="node1">
Configuring the cluster
Clustering can easily be enabled in tomcat by adding the following to the server.xml file
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
[turbo_widget widget-prefix=text&obj-class=WP_Widget_Text&widget-text–title=&widget-text–text=%3C!–+Post+Ad+–%3E%0A%3Cins+class%3D%22adsbygoogle%22%0A+++++style%3D%22display%3Ablock%22%0A+++++data-ad-client%3D%22ca-pub-4433761869744390%22%0A+++++data-ad-slot%3D%229020480262%22%0A+++++data-ad-format%3D%22auto%22%3E%3C%2Fins%3E%0A%3Cscript%3E%0A(adsbygoogle+%3D+window.adsbygoogle+%7C%7C+%5B%5D).push(%7B%7D)%3B%0A%3C%2Fscript%3E&widget-text–filter=false]
This will also enable session replication on all tomcat nodes. Below is a detailed example that shows more options, including multicast and deployer configuration. See the tomcat documentation for more info.
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true" /> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.8" port="45564" frequency="500" dropTime="3000" /> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="192.168.1.10" port="4200" autoBind="100" selectorTimeout="5000" maxThreads="6" /> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" /> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" /> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor" /> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=".*.gif;.*.js;.*.jpg;.*.png;.*.htm;.*.html;.*.css;.*.txt;" /> <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false" /> <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener" /> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" /> </Cluster>
Configuring session persistence (Optional)
Session persistence in configured in the context.xml file by defining a persistence manager. By default the session is persisted in-memory but optionally a disk or database storage can be also be configured. The example below shows how the session is persisted on the local disk using a session store.
<Manager className="org.apache.catalina.session.PersistentManager"> <Store className="org.apache.catalina.session.FileStore" directory="/home/kamran/tomcat/cluster" /> </Manager>
Making the application distributable
The java web applications can be configured to run in a cluster by adding the following to application’s web.xml file.
<distributable />
Once all this configuration is done on each node, the servers will be running in a cluster with session replication enabled.
[turbo_widget widget-prefix=text&obj-class=WP_Widget_Text&widget-text–title=&widget-text–text=%3C!–+Post+Ad+–%3E%0A%3Cins+class%3D%22adsbygoogle%22%0A+++++style%3D%22display%3Ablock%22%0A+++++data-ad-client%3D%22ca-pub-4433761869744390%22%0A+++++data-ad-slot%3D%229020480262%22%0A+++++data-ad-format%3D%22auto%22%3E%3C%2Fins%3E%0A%3Cscript%3E%0A(adsbygoogle+%3D+window.adsbygoogle+%7C%7C+%5B%5D).push(%7B%7D)%3B%0A%3C%2Fscript%3E&widget-text–filter=false]
Load balancing the cluster
Now the next step is to configure load balancing to effectively use the scalable tomcat cluster and to ensure high availability. Meaning if one of the nodes, in the cluster, goes down, the application will still be available on the other node. And with session replication enabled the service and user experience will not be impacted.
In this example I am using Apache’s mod_proxy_ajp to load balance the two tomcat instances. The configuration is fairly simple and is added to the httpd.conf file. The example also shows how session sticky-ness is enabled. The loadfactor defines the weighted load that is applied on each node. The lbmethod configures the load balancing strategy, which can either be byrequests or bytraffic.
<Proxy balancer://cluster> BalancerMember ajp://192.168.1.10:8009 loadfactor=1 route=node1 retry=60 BalancerMember ajp://192.168.1.11:8009 loadfactor=2 route=node2 retry=60 ProxySet lbmethod=bytraffic ProxySet stickysession=JSESSIONID|jsessionid </Proxy> ProxyPass /app balancer://cluster/app ProxyPass / balancer://cluster/app
Note: mod_proxy_ajp is not the only way apache is used to load balance tomcat instances, the other commonly used methods are mod_jk and mod_proxy.