Spring Boot 3部署外部Tomcat10会话集群异常:Spring服务会话不共享
核心原因分析
1. Tomcat Cluster配置错误(Manager位置放错)
你的Tomcat1的server.xml中,DeltaManager被错误嵌套在Channel标签内部,而正确位置应该是Cluster的直接子元素,与Channel同级。这个配置错误会导致Tomcat1无法正确初始化集群会话管理器,进而无法参与会话复制。
Tomcat1的错误配置片段:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <!-- 错误:Manager放在了Channel内部 --> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> ... </Channel> ... </Cluster>
2. Spring Boot自动配置干扰Tomcat会话管理
Spring Boot默认会为嵌入式容器配置会话管理器,即使部署到外部Tomcat,部分自动配置仍可能生效,导致Tomcat的集群会话管理器无法接管会话处理。
3. 缺少Web层面的distributable声明
虽然你在Tomcat的Context中设置了distributable="true",但如果WAR包的web.xml中没有<distributable/>标签,Servlet规范要求的分布式会话标识不完整,会导致Spring Boot的会话处理不触发集群复制。
解决方案
1. 修正Tomcat Cluster的Manager配置位置
将Tomcat1的DeltaManager移到Cluster标签下,与Channel同级,和Tomcat2的配置保持一致:
修正后的Tomcat1 Cluster配置片段:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"> <!-- 正确:Manager作为Cluster的直接子元素 --> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <!-- 移除Channel内部的Manager --> <Receiver address="127.0.0.1" port="3200" autoBind="100" selectorTimeout="5000" maxThreads="6" className="org.apache.catalina.tribes.transport.nio.NioReceiver"/> <!-- 其他Channel配置保持不变 --> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster>
2. 禁用Spring Boot的自动会话管理器配置
在Spring Boot启动类上排除会话自动配置,让外部Tomcat的集群管理器生效:
import org.springframework.boot.autoconfigure.session.SessionAutoConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication(exclude = {SessionAutoConfiguration.class}) public class YourApplication extends SpringBootServletInitializer { // 原有代码保持不变 }
3. 添加Web层面的distributable声明
在项目src/main/webapp/WEB-INF目录下创建web.xml文件,添加分布式会话标识:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0"> <distributable/> </web-app>
4. 验证会话ID格式
测试时检查返回的sessionId是否包含jvmRoute后缀(如xxxx.tomcat1、xxxx.tomcat2),这表明Tomcat的集群会话管理器已正确识别会话归属。如果会话ID前后不一致,需检查Tomcat的集群端口(3200、3201)是否被防火墙或其他进程占用。
内容的提问来源于stack exchange,提问作者watercolor

