session共享配置
Session 共享配置:Tomcat 集群与 Redis 存储方案
在分布式系统中,Session 共享是核心问题之一。当用户请求被负载均衡分发到不同服务器时,若 Session 未共享,会导致用户反复登录、状态丢失。本文详细讲解两种主流的 Session 共享方案:Tomcat 原生集群复制和Redis 集中存储,分析其原理、配置方法及适用场景。
Tomcat 原生集群:基于 Session 复制实现共享
Tomcat 通过内置的集群组件(SimpleTcpCluster)实现 Session 自动复制,多个 Tomcat 节点组成集群,当某节点的 Session 发生变化时,会同步到其他节点。
核心配置步骤
(1)修改server.xml,启用集群功能
在Engine或Host标签内添加集群配置(所有 Tomcat 节点配置一致):
<!-- tomcat集群节点
channelSendOptions 可以设置为2、4、8、10
- 2 确认发送
- 4 同步发送
- 8 异步发送
- 10 异步发送,且通过加上Acknowledge来提供可用性
-->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
<!-- Manager管理集群的session信息
提供了两种Manager:BackupManager和DeltaManager
- BackupManager:集群下的所有session放在一个备份节点,集群下所有节点都访问此备份节点
- DeltaManager:集群下某一节点生成、改动的session,将复制到其他节点,默认使用该种
每个节点部署的应用一样则使用DeltaManager;每个节点部署的应用不一样则使用BackupManager
expireSessionsOnShutdown 设置为true时,一个节点关闭,将导致集群下所有session失效
notifyListenersOnReplication 集群下节点间session复制、删除操作,是否通知session listeners
maxInactiveInterval 集群下Session的有效时间(单位:s),maxInactiveInterval内未活动的Session,将被Tomcat回收。默认值为1800(30min)
-->
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false" <!-- 节点关闭时不失效其他节点的Session -->
notifyListenersOnReplication="true"/> <!-- 复制时通知Session监听器 -->
<!-- 节点通信通道 -->
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<!-- Membership(组播方式) 维护集群的可用节点列表,可以检查到新增的节点,也可以检查到没有心跳的节点
address 组播地址
port 组播端口
frequency 心跳的间隔时间,单位是ms,默认500
dropTime 在dropTime时间内没有收到某个节点的心跳,则将该节点删除
-->
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4" <!-- 组播地址(集群内统一) -->
port="45564" <!-- 组播端口 -->
frequency="500" <!-- 心跳间隔(ms) -->
dropTime="3000"/> <!-- 超时未收到心跳则移除节点(ms) -->
<!-- Receiver 接收器,负责接收消息,分为阻塞式BioReceiver和非阻塞式NioReceiver
address 接收消息的地址
port 接收消息的端口
autoBind 端口的变化区间,如果port是4000,autoBind为100,则接收器将在4000-4099间取一个端口,进行监听
selectorTimeout NioReceiver轮询的超时时间
maxThreads 线程池的最大线程数
-->
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto" <!-- 自动获取本机IP -->
port="4000" <!-- 接收端口(不同节点需不同,如4000、4001) -->
autoBind="100" <!-- 端口冲突时自动偏移范围(4000-4099) -->
selectorTimeout="5000"
maxThreads="6"/>
<!-- Sender 发送器,负责发送消息
Sender内嵌了Transport组件,Transport真正负责发送消息
Transport分为两种:PooledParallelSender非阻塞式和PooledMultiSender阻塞式
-->
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<!-- Interceptor 集群的拦截器(增强可靠性)
TcpFailureDetector 网络、系统比较繁忙时,Membership可能无法及时更新可用节点列表,此时TcpFailureDetector可以拦截到某个节点关闭的信息,并尝试通过TCP连接此节点,以确保此节点真正的关闭,从而更新可用节点列表
-->
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<!-- MessageDispatch15Interceptor 查看Cluster组件发送消息的方式channelSendOptions是否设置为8,如果为8,MessageDispatch15Interceptor先将等待发送的消息进行排队,然后将排好队的消息转给Sender -->
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<!-- 集群部署器(同步应用部署) -->
<!-- Deployer 部署
tempDir 应用部署的临时目录,与其他实例同步来的应用文件会先存放在这里
deployDir 应用的部署目录,与Host配置的appBase一致
watchDir 监控目录,当watchEnabled为true时,该目录下应用的变化会同步到集群中的各个实例
-->
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="true"/>
<!-- ClusterListener 监听器,监听Cluster组件接收的消息,使用DeltaManager时,Cluster接收的信息通过ClusterSessionListener传递给DeltaManager -->
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
(2)为每个 Tomcat 节点设置唯一标识(jvmRoute)
在server.xml的Engine标签中添加jvmRoute,区分不同节点:
<!-- 节点1:jvmRoute=tomcat1 -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
<!-- 节点2:jvmRoute=tomcat2 -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">
(3)应用声明支持分布式
在应用的web.xml中添加<distributable/>标签,告知 Tomcat 该应用需要 Session 复制:
<web-app>
<!-- 声明应用支持集群分布式 -->
<distributable/>
<!-- 其他配置... -->
</web-app>
原理与优缺点
- 原理:当某节点的 Session 被创建或修改时,
DeltaManager会将变更的 Session 数据通过组播方式复制到集群内其他节点,确保所有节点的 Session 一致。 - 优点:
- 无需额外组件,纯 Tomcat 原生支持;
- 配置简单,适合小规模集群(3-5 节点)。
- 缺点:
- 集群节点增多时,Session 复制的网络开销急剧增大(O (n²) 复杂度);
- 仅支持同构应用(所有节点部署相同应用);
- 不适合 Session 数据量大的场景(复制耗时)。
Redis 集中存储:基于缓存中间件实现共享
通过 Redis 作为 Session 的统一存储介质,所有 Tomcat 节点从 Redis 读写 Session,避免复制开销,适合大规模集群。
核心配置步骤
(1)添加依赖 JAR 包
将以下 JAR 包放入 Tomcat 的lib目录(版本需匹配):
tomcat-redis-session-manager.jar(Tomcat-Redis 集成工具)jedis-3.6.0.jar(Redis 客户端)commons-pool2-2.11.1.jar(连接池依赖)
(2)修改context.xml,配置 Redis 连接
在context.xml中添加 Redis Session 管理器:
<Context>
<!-- Redis Session处理器 -->
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<!-- Redis Session管理器 -->
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="127.0.0.1" <!-- Redis主机IP -->
port="6379" <!-- Redis端口 -->
database="0" <!-- Redis数据库编号 -->
password="your_redis_pwd" <!-- Redis密码(无密码可省略) -->
maxInactiveInterval="1800" <!-- Session过期时间(秒) -->
timeout="2000" <!-- Redis连接超时(毫秒) -->
poolSize="10" <!-- 连接池大小 -->
sentinelMaster="mymaster" <!-- 哨兵模式主节点名称(集群时配置) -->
sentinels="192.168.1.10:26379,192.168.1.11:26379"/> <!-- 哨兵节点列表 -->
</Context>
(3)无需修改应用代码
应用无需添加<distributable/>,Session 的创建、获取逻辑与单机一致,底层自动通过 Redis 存储。
原理与优缺点
- 原理:Tomcat 将 Session 的创建、读取、更新、删除操作委托给 Redis,所有节点共享同一个 Redis 实例,实现 Session 数据的集中管理。
- 优点:
- 无复制开销,适合大规模集群(节点数无限制);
- 支持 Session 持久化(Redis 开启 RDB/AOF);
- 可跨语言共享(如 Tomcat、Node.js 等不同服务共享 Session)。
- 缺点:
- 依赖 Redis 服务(需保证高可用,可通过哨兵或集群实现);
- 增加网络开销(每次 Session 操作需访问 Redis)。
方案对比与选择建议
| 维度 | Tomcat 原生集群 | Redis 集中存储 |
|---|---|---|
| 集群规模 | 适合小规模(≤5 节点) | 支持大规模(不限节点) |
| 网络开销 | 高(节点间复制) | 中(仅与 Redis 通信) |
| 可靠性 | 依赖组播 / 节点存活 | 依赖 Redis 高可用 |
| 数据一致性 | 强一致性(实时复制) | 最终一致性(Redis 单线程) |
| 适用场景 | 小型同构应用集群 | 大型分布式系统、跨语言服务 |
选择建议:
- 中小规模 Java 应用集群,优先用 Tomcat 原生集群(配置简单,无额外依赖);
- 大规模集群、跨语言服务或 Session 数据量大的场景,选择 Redis 集中存储(扩展性更好)。
注意事项
- Session 序列化:
两种方案均要求 Session 中的对象实现Serializable接口,否则无法正常复制或存储。 - Redis 高可用:
生产环境需部署 Redis 哨兵或集群,避免单点故障导致 Session 丢失。 - 性能优化:
- Tomcat 集群:减少 Session 数据量,避免频繁修改(如将大对象存入数据库,Session 仅存 ID);
- Redis 方案:合理设置连接池大小,启用 Redis Pipeline 减少网络往返。