Skip to main content

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​

FeatureDeltaManagerBackupManager
ReplicationAll-to-AllPrimary + Backup
Replication targetAll nodes1 backup node only
Best forSmall scale (2~4 servers)Medium scale (4+ servers)
On failureAny server can take overBackup takes over from Primary
Memory/Network loadHighLow

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.