0%

session共享配置

Session 共享配置:Tomcat 集群与 Redis 存储方案

在分布式系统中,Session 共享是核心问题之一。当用户请求被负载均衡分发到不同服务器时,若 Session 未共享,会导致用户反复登录、状态丢失。本文详细讲解两种主流的 Session 共享方案:Tomcat 原生集群复制Redis 集中存储,分析其原理、配置方法及适用场景。

Tomcat 原生集群:基于 Session 复制实现共享

Tomcat 通过内置的集群组件(SimpleTcpCluster)实现 Session 自动复制,多个 Tomcat 节点组成集群,当某节点的 Session 发生变化时,会同步到其他节点。

核心配置步骤

(1)修改server.xml,启用集群功能

EngineHost标签内添加集群配置(所有 Tomcat 节点配置一致):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<!-- 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.xmlEngine标签中添加jvmRoute,区分不同节点:

1
2
3
4
5
<!-- 节点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 复制:

1
2
3
4
5
<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 管理器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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 集中存储(扩展性更好)。

注意事项

  1. Session 序列化
    两种方案均要求 Session 中的对象实现Serializable接口,否则无法正常复制或存储。
  2. Redis 高可用
    生产环境需部署 Redis 哨兵或集群,避免单点故障导致 Session 丢失。
  3. 性能优化
    • Tomcat 集群:减少 Session 数据量,避免频繁修改(如将大对象存入数据库,Session 仅存 ID);
    • Redis 方案:合理设置连接池大小,启用 Redis Pipeline 减少网络往返。

欢迎关注我的其它发布渠道