session共享配置

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

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

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

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

核心配置步骤

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

EngineHost标签内添加集群配置(所有 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.xmlEngine标签中添加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 集中存储(扩展性更好)。

注意事项

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