200字
Tomcat Session Replication Cluster
2026-03-17
2026-03-17

Tomcat Session Replication Cluster

Tomcat 官方实现了 Session 的复制集群,将每个Tomcat的Session进行相互的复制同步,从而保证所有Tomcat都有相同的Session信息

图片-JgHVasQLbKzbVsMThyXMQoiLQQyfMDxx.png

配置说明

官方文档:

https://tomcat.apache.org/tomcat-10.0-doc/cluster-howto.html
https://tomcat.apache.org/tomcat-9.0-doc/cluster-howto.html
https://tomcat.apache.org/tomcat-8.5-doc/cluster-howto.html
http://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html

修改 conf/server.conf

<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" #45564/UDP
                    frequency="500" #间隔500ms发送
                    dropTime="3000"/> #故障阈值3s
        <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                  address="auto" #监听地址,此项建议修改为当前主机的IP(不支持0.0.0.0),如果不修改可能会导致服务无法启动
                  port="4000" #监听端口
                  autoBind="100" #如果端口冲突,自动绑定其它端口,范围是4000-4100
                  selectorTimeout="5000" #自动绑定超时时长5s
                  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=""/>
    <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
    <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.ClusterSessionListener"/>
</Cluster>
#注意:tomcat7的官方文档此处有错误
http://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html
......
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener">
    <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener">
        </Cluster>

配置说明

  • Cluster 集群配置
  • Manager 会话管理器配置
  • Channel 信道配置
    • Membership 成员判定。使用什么多播地址、端口多少、间隔时长ms、超时时长ms。同一个多播地址和端口认为同属一个组。使用时修改这个多播地址,以防冲突
    • Receiver 接收器,多线程接收多个其他节点的心跳、会话信息。默认会从4000到4100依次尝试可用端口
      • address="auto",auto可能绑定到127.0.0.1上,所以一定要改为当前主机可用的IP
    • Sender 多线程发送器,内部使用了tcp连接池。
    • Interceptor 拦截器
  • Valve
    • ReplicationValve 检测哪些请求需要检测Session,Session数据是否有了变化,需要启动复制过程
  • ClusterListener
    • ClusterSessionListener 集群session侦听器

使用 <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>

添加到 <Engine> 所有虚拟主机都可以启用Session复制

添加到 <Host> ,该虚拟主机可以启用Session复制

最后,在应用程序内部启用了才可以使用

修改应用目录下的 WEB-INF/web.xml

注意: 不要修改 conf/web.xml,此文件不起作用

注意:WEB-INF/web.xml文件的如果权限不对,会导致复制失败

	</description>
<distributable/> #添加此行
</web-app>

实战案例: 实现 Tomcat Session 集群2

环境准备:

  • 时间同步,确保NTP或Chrony服务正常运行
  • 防火墙规则
IP主机名服务组件/软件
10.0.0.100proxy.ayaka.org调度器Nginx、HTTPD
10.0.0.101t1.ayaka.orgtomcat1JDK8、Tomcat8
10.0.0.102t2.ayaka.orgtomcat2JDK8、Tomcat8

在 proxy 主机设置 httpd (或nginx)实现后端tomcat主机轮询

[root@proxy ~]#cat /etc/httpd/conf.d/tomcat.conf
<Proxy balancer://tomcat-server>
	BalancerMember http://t1.wang.org:8080 loadfactor=1
	BalancerMember http://t2.wang.org:8080 loadfactor=1
</Proxy>

<VirtualHost *:80>
    ServerName proxy.wang.org
    ProxyRequests Off
    ProxyVia On
    ProxyPreserveHost On
    ProxyPass / balancer://tomcat-server/
    ProxyPassReverse / balancer://tomcat-server/
    </VirtualHost>
    
[root@proxy ~]#systemctl restart httpd

在所有后端tomcat主机上修改conf/server.xml

本次把多播复制的配置放到t1.wang.org和t2.wang.org虚拟主机里面, 即Host块中。

特别注意修改Receiver的address属性为一个本机可对外的IP地址。

修改 t1 主机的 conf/server.xml

#将前面内容复制到conf/server.xml的Host块内或Engine块(针对所有主机)
[root@t1 ~]#vim /usr/local/tomcat/conf/server.xml
[root@t1 ~]#cat /usr/local/tomcat/conf/server.xml
.....以上省略.....
	<Host name="t1.wang.org" appBase="/data/webapps" unpackWARs="true" autoDeploy="true">
  ###################在<Host> </host>块中间加下面一段内容##############################
    <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="230.100.100.100" #指定不冲突的多播地址
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
            
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="10.0.0.101" #监听地址,此项建议修改为当前主机的IP,如果不修改可能会导致服务无法启动
                      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=""/>
        <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
        
        <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.ClusterSessionListener"/>
    </Cluster>
        #######################################以上内容是新加的##########################
    </Host>
  </Engine>
 </Service>
</Server>
       
[root@t1 ~]#systemctl restart tomcat
[root@t1 ~]#ss -ntl
State Recv-Q Send-Q Local Address:Port Peer
Address:Port
LISTEN 0 128 0.0.0.0:22
0.0.0.0:*
LISTEN 0 100 127.0.0.1:25
0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 100 [::1]:25 [::]:*
LISTEN 0 50 [::ffff:10.0.0.101]:4000 *:*
LISTEN 0 1 ::ffff:127.0.0.1]:8005 *:*
LISTEN 0 100 *:8009 *:*
LISTEN 0 100 *:8080 *:*

简化说明

t1的conf/server.xml中,如下

<Host name="t1.wang.org" appBase="/data/webapps" autoDeploy="true" >
#其他略去
    <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
              address="10.0.0.101" #只改此行
              port="4000"
              autoBind="100"
              selectorTimeout="5000"
              maxThreads="6"/>

修改 t2 主机的 conf/server.xml

[root@t2 ~]#vim /usr/local/tomcat/conf/server.xml
[root@t2 ~]#cat /usr/local/tomcat/conf/server.xml
.....以上省略.....
	<Host name="t2.wang.org" appBase="/data/webapps" unpackWARs="true" autoDeploy="true">
  ###################在<Host> </host>块中间加下面一段内容##############################
    <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="230.100.100.100" #指定不冲突的多播地址
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
            
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="10.0.0.102" #监听地址,此项建议修改为当前主机的IP,如果不修改可能会导致服务无法启动
                      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=""/>
        <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
        
        <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.ClusterSessionListener"/>
    </Cluster>
        #######################################以上内容是新加的##########################
    </Host>
  </Engine>
 </Service>
</Server>
       
[root@t1 ~]#systemctl restart tomcat
[root@t1 ~]#ss -ntl
State Recv-Q Send-Q Local Address:Port Peer
Address:Port
LISTEN 0 128 0.0.0.0:22
0.0.0.0:*
LISTEN 0 100 127.0.0.1:25
0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 100 [::1]:25 [::]:*
LISTEN 0 50 [::ffff:10.0.0.101]:4000 *:*
LISTEN 0 1 ::ffff:127.0.0.1]:8005 *:*
LISTEN 0 100 *:8009 *:*
LISTEN 0 100 *:8080 *:*

简化说明

t2主机的server.xml中,如下

<Host name="t1.wang.org" appBase="/data/webapps" autoDeploy="true" >
#其他略去
    <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
              address="10.0.0.102" #只改此行
              port="4000"
              autoBind="100"
              selectorTimeout="5000"
              maxThreads="6"/>

尝试使用刚才配置过得负载均衡(移除Session黏性),测试发现Session还是变来变去。

修改应用目录下的 WEB-INF/web.xml文件开启该应用程序的分布式

参考官方说明: https://tomcat.apache.org/tomcat-8.5-doc/cluster-howto.html

Make sure your web.xml has the <distributable/> element

为所有tomcat主机应用web.xml的 <web-app> 标签增加子标签 <distributable/> 来开启该应用程序的分布式。

修改t1主机的应用目录下的 WEB-INF/web.xml文件

[root@t1 ~]#ll /usr/local/tomcat/webapps/ROOT/WEB-INF/
total 4
-rw-r----- 1 tomcat tomcat 1227 Jul 1 05:53 web.xml

#保留权限
[root@t1 ~]#cp -a /usr/local/tomcat/webapps/ROOT/WEB-INF/ /data/webapps/ROOT/
/data/webapps/ROOT/
├── index.jsp
└── WEB-INF
	└── web.xml
#在倒数第二行加一行
[root@t1 ~]##vim /data/webapps/ROOT/WEB-INF/web.xml
[root@t1 ~]#tail -n3 /data/webapps/ROOT/WEB-INF/web.xml
	</description>
<distributable/> #添加此行
</web-app>

#注意权限
[root@t1 ~]#ll /data/webapps/ROOT/WEB-INF/
total 4
-rw-r----- 1 tomcat tomcat 1243 Jan 17 09:37 web.xml
#生效
[root@t1 ~]#systemctl restart tomcat

#同时观察日志
[root@t1 ~]#tail -f /usr/local/tomcat/logs/catalina.out
15-Jul-2020 11:29:10.998 INFO [Membership-MemberAdded.]
org.apache.catalina.ha.tcp.SimpleTcpCluster.memberAdded Replication member added:
[org.apache.catalina.tribes.membership.MemberImpl[tcp://{10, 0, 0, 102}:4000,
{10, 0, 0, 102},4000, alive=1022, securePort=-1, UDP Port=-1, id={89 -26 -30 -99
16 80 65 95 -65 14 -33 124 -55 -123 -30 82 }, payload={}, command={}, domain=
{}]]

修改t2主机的应用目录下的 WEB-INF/web.xml 文件

#与t1相同的操作
[root@t2 ~]#cp -a /usr/local/tomcat/webapps/ROOT/WEB-INF/ /data/webapps/ROOT/
[root@t2 ~]#vim /data/webapps/ROOT/WEB-INF/web.xml
[root@t2 ~]#tail -n3 /data/webapps/ROOT/WEB-INF/web.xml
	</description>
<distributable/> #倒数第二行添加此行
</web-app>

#注意权限
[root@t2 ~]#ll /data/webapps/ROOT/WEB-INF/
total 4
-rw-r----- 1 tomcat tomcat 1243 Jan 17 09:38 web.xml

#生效
[root@t2 ~]#systemctl restart tomcat

#同时观察日志
[root@t2 ~]#tail -f /usr/local/tomcat/logs/catalina.out
15-Jul-2020 11:29:12.088 INFO [t2.wang.org-startStop-1]
org.apache.catalina.ha.session.DeltaManager.getAllClusterSessions Manager [],
requesting session state from
[org.apache.catalina.tribes.membership.MemberImpl[tcp://{10, 0, 0, 101}:4000,
{10, 0, 0, 101},4000, alive=208408, securePort=-1, UDP Port=-1, id={118 -108
-116 119 58 22 73 113 -123 -96 -94 111 -65 -90 -87 -107 }, payload={}, command=
{}, domain={}]]. This operation will timeout if no session state has been
received within [60] seconds.

测试访问

重启全部Tomcat,通过负载均衡调度到不同节点,返回的SessionID不变了。

用浏览器访问,并刷新多次,发现SessionID 不变,但后端主机在轮询

#修改t1和t2的配置项,删除jvmRoute配置项
[root@t1 tomcat]#vim conf/server.xml
<Engine name="Catalina" defaultHost="t1.wang.org" >
[root@t1 tomcat]#systemctl restart tomcat

#多次执行下面操作,可以看到SessionID不变
[root@centos7 ~]#curl -b 'JSESSIONID=1A3E7EED14F3E44FAF7469F8693E1CB6' proxy.wang.org/index.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>tomcat test</title>
</head>
<body>
<h1> tomcat website </h1>
<div>On tomcat-server</div>
<div>10.0.0.102:8080</div>
<div>SessionID = <span
style="color:blue">1A3E7EED14F3E44FAF7469F8693E1CB6</span></div>
Wed Jul 15 11:33:09 CST 2020
</body>
</html>
[root@centos7 ~]#curl -b 'JSESSIONID=1A3E7EED14F3E44FAF7469F8693E1CB6' proxy.wang.org/index.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>tomcat test</title>
</head>
<body>
<h1> tomcat website </h1>
<div>On tomcat-server</div>
<div>10.0.0.101:8080</div>
<div>SessionID = <span
style="color:blue">1A3E7EED14F3E44FAF7469F8693E1CB6</span></div>
Wed Jul 15 11:33:10 CST 2020
</body>
</html>
[root@centos7 ~]#

故障模拟

#模拟t2节点故障
[root@t2 ~]#systemctl stop tomcat
#多次访问SessionID不变
[root@centos7 ~]#curl -b 'JSESSIONID=1A3E7EED14F3E44FAF7469F8693E1CB6' proxy.wang.org/index.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>tomcat test</title>
</head>
<body>
<h1> tomcat website </h1>
<div>On tomcat-server</div>
<div>10.0.0.101:8080</div>
<div>SessionID = <span
style="color:blue">1A3E7EED14F3E44FAF7469F8693E1CB6</span></div>
Wed Jul 15 12:01:16 CST 2020
</body>
</html>

恢复实验环境

为学习后面的内容,删除此节相关配置,为后续内容准备

#恢复t1环境
[root@t1 ~]#vim /usr/local/tomcat/conf/server.xml
[root@t1 ~]#tail /usr/local/tomcat/conf/server.xml
            <Valve className="org.apache.catalina.valves.AccessLogValve" 
                   directory="logs"
                   prefix="localhost_access_log" 
                   suffix=".txt"
                   pattern="%h %l %u %t &quot;%r&quot; %s %b" />
        </Host>

        <Host name="t1.wang.org" appBase="/data/webapps" unpackWARs="true" autoDeploy="true">
            </Host>

    </Engine>
  </Service>
</Server>
[root@t1 ~]#rm -f /data/webapps/ROOT/WEB-INF/web.xml
[root@t1 ~]#systemctl restart tomcat

#恢复t2环境
[root@t2 ~]#vim /usr/local/tomcat/conf/server.xml
[root@t2 ~]#tail /usr/local/tomcat/conf/server.xml
<Valve className="org.apache.catalina.valves.AccessLogValve" 
                   directory="logs"
                   prefix="localhost_access_log" 
                   suffix=".txt"
                   pattern="%h %l %u %t &quot;%r&quot; %s %b" />
        </Host>

        <Host name="t2.wang.org" appBase="/data/webapps" unpackWARs="true" autoDeploy="true">
            </Host>

    </Engine>
  </Service>
</Server>
[root@t2 ~]#rm -f /data/webapps/ROOT/WEB-INF/web.xml
[root@t2 ~]#systemctl restart tomcat

评论