This configuration is tested on Tomcat 8.5.31.
server.xml
<Engine name="Catalina" defaultHost="localhost" jvmRoute="test01"> <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.4" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="10.10.10.10" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster> </Engine>
Some important points for test
- Engine’s jvmRoute attribte must be unique among the same cluster nodes. Tomcat generates session id (JSESSIONID cookie) with following format : session_id.jvmRoute
- Manager’s className could be org.apache.catalina.ha.session.BackupManager or org.apache.catalina.ha.session.DeltaManager. It depends on which cluster type you use
- Membership’s address and port is multicast address. The same cluster instances must use the same value. (Don’t share it with different cluster)
- Receiver’s address and port is used as listen port for peer to peer network.
- The role of ReplicationValve is to broadcast updated session data among cluster nodes.
- The role of JvmRouteBinderValve is to change session id if incoming session’s master node is different from the processing node. (ex. if node#1 generated session#A and node#2 receives session#A, then node#2 updates session#A’s session id)
web.xml
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" > <distributable/>
web.xml is inside each webapp. If distributable is not defined, that webapp’s session data is not shared among cluster
More on cluster
- Tomcat uses Tribe as cluster framework. (similar to jGroups)
- Membership’s information is used to manage cluster nodes. Each node broadcast it’s name and additional info including listen port (Receiver attributes) And also heartbeat message is broadcasted periodically. (Which is used to detect node failure) Some network doesn’t allow broadcast address between VLANs. Therefore check network administrator in advance.
- After all nodes joined the cluster, each node sends session data based on Sender and Receiver attributes.(This is peer to peer TCP/IP, not broadcast) Therefore Receiver’s ip and port must be accessible from other nodes. (Do not use 127.0.0.1 as Receiver’s ip)
How to check if session cluster is working
After staring Tomcat, check catalina.out if cluster is enabled.
INFO [main] org.apache.catalina.ha.tcp.SimpleTcpCluster.startInternal Cluster is about to start INFO [main] org.apache.catalina.tribes.transport.ReceiverBase.bind Receiver Server Socket bound to:[/x.x.x.x:4000] INFO [main] org.apache.catalina.tribes.membership.McastServiceImpl.setupSocket Setting cluster mcast soTimeout to [500] INFO [main] org.apache.catalina.tribes.membership.McastServiceImpl.waitForMembers Sleeping for [1000] milliseconds to establish cluster membership, start level:[4] INFO [main] org.apache.catalina.tribes.membership.McastServiceImpl.waitForMembers Done sleeping, membership established, start level:[4] INFO [main] org.apache.catalina.tribes.membership.McastServiceImpl.waitForMembers Sleeping for [1000] milliseconds to establish cluster membership, start level:[8] INFO [main] org.apache.catalina.tribes.membership.McastServiceImpl.waitForMembers Done sleeping, membership established, start level:[8] |
And also check if target webapp is clustered
INFO [localhost-startStop-1] org.apache.catalina.ha.session.DeltaManager.startInternal Register manager [localhost#/session_testkit] to cluster element [Engine] with name [Catalina] |
Finally, check if other node joined the cluster
INFO [Membership-MemberAdded.] org.apache.catalina.ha.tcp.SimpleTcpCluster.memberAdded Replication member added:[org.apache.catalina.tribes.membership.MemberImpl[tcp://{x.x.x.x}:4000,{y.y.y.y},4000, alive=1010, securePort=-1, UDP Port=-1, id={-63 -97 -125 -89 -107 24 64 97 -69 -127 -12 38 0 -35 11 -94 }, payload={}, command={}, domain={}, ]] |
If a node stops, the following log is printed
INFO [Membership-MemberDisappeared.] org.apache.catalina.ha.tcp.SimpleTcpCluster.memberDisappeared Received member disappeared:[org.apache.catalina.tribes.membership.MemberImpl[tcp://{x.x.x.x}:4000,{x.x.x.x},4000, alive=688569, securePort=-1, UDP Port=-1, id={-63 -97 -125 -89 -107 24 64 97 -69 -127 -12 38 0 -35 11 -94 }, payload={}, command={66 65 66 89 45 65 76 69 88 …(9)}, domain={}, ]] |