Tomcat In-Memory Clustering
Tomcat provides built-in in-memory clustering via SimpleTcpCluster, which replicates sessions between servers in real time. You can implement session sharing through Tomcat configuration alone, without any external storage.
Clustering Architectureβ
The Tomcat cluster uses multicast or unicast to automatically discover members and replicates session changes to other servers in the cluster whenever they occur.
[App1: 10.0.0.1]
Session A changes
β
ββββΆ [App2: 10.0.0.2] Session A replicated
ββββΆ [App3: 10.0.0.3] Session A replicated
DeltaManager vs BackupManagerβ
| Feature | DeltaManager | BackupManager |
|---|---|---|
| Replication | All-to-All | Primary + Backup |
| Replication target | All nodes | 1 backup node only |
| Best for | Small scale (2~4 servers) | Medium scale (4+ servers) |
| On failure | Any server can take over | Backup takes over from Primary |
| Memory/Network load | High | Low |
DeltaManager Configurationβ
Replicates sessions to all servers. Simplest and most reliable.
server.xml (Apply identically to each Tomcat)β
<Engine name="Catalina" defaultHost="localhost" jvmRoute="server1">
<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.0.0.1"
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"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*\.gif;.*\.js;.*\.jpeg;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
<Host name="localhost" appBase="webapps" ...>
</Host>
</Engine>
Tomcat 2 and 3 (change address only)β
<!-- Tomcat 2 -->
<Engine jvmRoute="server2">
<Cluster ...>
<Channel>
<Receiver address="10.0.0.2" port="4000" .../>
</Channel>
</Cluster>
</Engine>
web.xml: Declare Distributable (Required)β
Add the <distributable/> tag to WEB-INF/web.xml for any web application using clustering:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://jakarta.ee/xml/ns/jakartaee" version="6.0">
<!-- This tag is required to enable session replication -->
<distributable/>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>
Unicast Configuration (When Multicast is Blocked)β
Multicast is blocked in cloud environments and some networks. Use static unicast membership instead.
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Interceptor className="org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor">
<Member className="org.apache.catalina.tribes.membership.StaticMember"
host="10.0.0.2"
port="4000"
uniqueId="{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}"/>
<Member className="org.apache.catalina.tribes.membership.StaticMember"
host="10.0.0.3"
port="4000"
uniqueId="{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3}"/>
</Interceptor>
<Receiver address="10.0.0.1" port="4000"
className="org.apache.catalina.tribes.transport.nio.NioReceiver"
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.MessageDispatchInterceptor"/>
</Channel>
Session Serialization: Serializable Requiredβ
Cluster-to-cluster session replication uses Java serialization. All objects stored in the session must implement java.io.Serializable.
public class UserSession implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Long userId;
private String username;
private String role;
private LocalDateTime loginTime;
// Exclude non-serializable fields with transient
private transient HttpSession httpSession;
// Default constructor required for deserialization
public UserSession() {}
// ...
}
Verifying Cluster Operationβ
# Check cluster member detection in Tomcat logs
grep "McastService" /opt/tomcat/logs/catalina.out
# INFO: Added member [/10.0.0.2:4000]
# INFO: Added member [/10.0.0.3:4000]
# Test session replication
# 1. Login on App1
curl -c cookies.txt -X POST http://app1:8080/app/login \
-d "user=admin&pass=secret"
# 2. Access App2 directly β session should still be valid
curl -b cookies.txt http://app2:8080/app/profile
# 3. Stop App1 and verify session survives
sudo systemctl stop tomcat1
curl -b cookies.txt http://lb.example.com/app/profile
# Should still be logged in because sessions were replicated
Limits of In-Memory Clusteringβ
Replication Load Grows with Server Countβ
DeltaManager replicates N-1 times for each session change across N servers.
3 servers: 1 session change β 2 replications (manageable)
10 servers: 1 session change β 9 replications (growing burden)
50 servers: 1 session change β 49 replications (severe burden)
Recommendation: Switch to BackupManager or Redis sessions for 4+ servers.
Network Partition Riskβ
If the network between cluster nodes is temporarily severed, each node operates independently and session data can diverge. Conflicts may occur when the network is restored.
The next page covers Redis-based external session sharing to overcome these limitations.