05.Session共享

什么是 Session 共享问题

在负载均衡环境中,请求会被分发到不同的 Tomcat 服务器,此时会出现以下问题:

  • 用户第一次访问 tomcat_8001 并登录成功。
  • 第二次访问被分配到 tomcat_8002,由于没有记录登录状态,用户会呈现未登录状态。
  • 客⼾端浏览器访问服务器的时候,服务器把客⼾端信息以某种形式记录在服务器上,这就是 Session。
  • 客⼾端浏览器再次访问时只需要从该 Session 中查找该客⼾的状态就可以了。
  • Cookie是服务器写给客⼾端的⽂件,也可以称为浏览器缓存。保存在客⼾端浏览器中,⽽ Session 保存在服务器上。

这种问题称为 Session 共享问题:分布式集群下,各节点需共享 Session 数据以保证用户状态一致性。

解决方案

session由服务器端⽣成并存储,当应⽤进⾏分布式集群部署的时候,如何保证不同服务器上session信息能够共享呢?

三种实现⽅式:

1. nginx的ip_hash算法

使⽤nginx的ip_hash算法,此算法将来自同一 IP 的请求统⼀发送到同⼀个节点服务器上。

在不考虑节点服务器宕机的情况,可不考虑session问题。但是,节点服务器宕机后,⽤⼾需要关掉浏览器从新打开登录才能恢复正常,这样体验会变得很差。

  • 优点:配置简单:在 upstream 模块中添加 ip_hash;
  • 缺点
    • 大量请求来自同一局域网时,负载均衡失效。
    • 如果分配的 Tomcat 服务器宕机,用户体验依然受影响。

2. Session 复制

Session复制是指session信息会在集群节点之间复制(同步),每个节点服务器上都会有相同的session信息。

即使⼀个节点服务器宕机了,只要还有服务器存活,就不影响⽤⼾使⽤。

优点: 配置简单,成本低

缺点: 缺点是node之间通信频繁,响应速度有影响,多并发、⾼频操作的情况下性能下降⽐较厉害。

tomcat⾃带集群中,提供了session复制,session信息会在各个tomcat中同步,对⽹络要求较⾼,session内存消耗影响会很⼤,对于⼩集群(节点数 ≤ 3)够⽤了。

3. session集中存储(基于Redis/Memcached/hbase等数据库的共享)

在实际⼯作中我们建议使⽤外部的缓存设备来共享 Session,Session数据都会放到外部缓存容器中,避免单个服务器节点挂掉⽽影响服务。

使⽤Redis实现共享session,所有服务器的session信息都存储到了同⼀个Redis集群中,⽆论是对 Session 的注销、更新都会同步到集群中,无论请求被分配到哪个 Tomcat 服务器,都能从 Redis 中获取 Session 信息,达到了 Session 共享的⽬的。

Redis + Tomcat Session Manager 解决方案

优点: 高性能、高可用,扩展性强

缺点:需额外维护缓存服务

redisson是redis官⽹推荐的java语⾔实现分布式锁的项⽬。当然,redisson远不⽌分布式锁,还包括其他⼀些分布式结构。例如,分布式应⽤,分布式缓存,分布式回话管理,分布式服务(任务,延迟任务,执⾏器),分布式redis客⼾端等。

Redis + Tomcat Session Manager

  • 原理:使用 Redis 存储 Session 信息,所有 Tomcat 服务器共享 Redis 中的 Session。
  • 步骤
    1. 启动 Redis

      • 下载并运行 Redis 服务器(如 redis-server.exe)。
    2. 添加 Jar 包

      • 将以下 Jar 包放入 Tomcat 的 lib 目录:
        • jedis-2.5.2.jar
        • commons-pool2-2.0.jar
        • tomcat-redis-session-manager1.2.jar
    3. 修改 context.xml

      • tomcat/conf/context.xml 中添加以下配置:

        <Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
        <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
                host="127.0.0.1"
                port="6379"
                database="0"
                maxInactiveInterval="60" />
        

        是否需要配置 Redis 用户名和密码?

        • 默认情况:Redis 不需要用户名和密码,只需配置 hostport
        • 启用密码认证:如果 Redis 设置了密码,需要在 context.xml 中配置 password
        • 启用 ACL 认证:如果 Redis 启用了 ACL,需要在 context.xml 中同时配置 usernamepassword
        • Redis 6.0 之前仅支持密码认证,不支持用户名认证。
        • Redis 6.0 引入了 ACL(访问控制列表),支持用户名和密码认证。
    4. 重启 Tomcat

      • 重启所有 Tomcat 服务器以应用配置。
    5. 测试

      • 访问 tomcat_8001 并登录,然后访问 tomcat_8002,检查是否保持登录状态。

redisson提供的session方案

提前先安装好nginx+tomcat+jdk+redis。

修改redis配置文件 /etc/redis.conf

bind 0.0.0.0
daemonize yes
protected-mode no

这里访问redisson项目的redisson-tomcat目录: https://github.com/redisson/redisson/tree/master/redisson-tomcat

image-20250328001809704

帮助页面地址:https://redisson.pro/docs/web-session-management/

在帮助页面下载需要的两个jar包:redisson-all-3.41.0.jar、redisson-tomcat-9-3.41.0.jar

image-20250328001901677

将下载好的jar包添加到tomcat的lib目录下。

redisson-all-3.41.0.jar、redisson-tomcat-9-3.41.0.jar、fst-2.57.jar

修改tomcat配置文件conf/context.xml

<Manager className="org.redisson.tomcat.RedissonSessionManager" configPath="${catalina.base}/conf/redisson.json" readMode="REDIS" updateMode="DEFAULT"/>

创建配置文件conf/redisson.json

{
  "singleServerConfig":{
    "idleConnectionTimeout":10000,
    "connectTimeout":10000,
    "timeout":3000,
    "retryAttempts":3,
    "retryInterval":1500,
    "password":null,
    "subscriptionsPerConnection":5,
    "clientName":null,
    "address": "redis://127.0.0.1:6379",
    "subscriptionConnectionMinimumIdleSize":1,
    "subscriptionConnectionPoolSize":50,
    "connectionMinimumIdleSize":32,
    "connectionPoolSize":64,
    "database":0,
    "dnsMonitoringInterval":5000
  },
  "threads":0,
  "nettyThreads":0,
  "codec":{
    "class":"org.redisson.codec.FstCodec"
  },
  "transportMode":"NIO"
}

在tomcat中添加测试页面testsession.jsp

 <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
 <%
  String path = request.getContextPath();
  String basePath = request.getScheme() + "://"
    + request.getServerName() + ":" + request.getServerPort() + path + "/";
 %>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
    <title>tomcat1</title>
  </head>

  <body>
  <center><h1>tomcat1</h1></center>
  <center>
    <h3>sessionId:</h3><%=session.getId()%>
    <h3>session创建时间:</h3><%=session.getCreationTime()%>
  <center>
  </body>
</html>

在nginx中配置负载均衡

然后多次访问nginx的虚拟主机,查看在分配给不同tomcat主机的情况下,页面显示的sessionid是否一致。

redisson的序列化

在使用 Redisson 的 Web 会话管理(如 Tomcat Session 共享)时,需要引入 fst-2.57.jar

  • FST(Fast Serialization Toolkit) 是一个高性能的 Java 对象序列化框架,具有以下优势:

    • 速度快:比 JDK 原生序列化快 10 倍以上。
    • 体积小:序列化后的字节数据更小,适合网络传输和存储。
    • 兼容性:支持跨版本反序列化(需注意版本兼容性)。
  • Redisson 的依赖
    Redisson 默认支持多种序列化方式,其中 FstCodec(基于 FST)是性能最优的选择之一。

若在 Redisson 的配置文件(如 redisson.jsonredisson.yml)中,若指定了序列化为 FstCodec

"codec": {
    "class": "org.redisson.codec.FstCodec"
}

此时必须依赖 fst-2.57.jar,否则会抛出以下错误:

ClassNotFoundException: com.caucho.hessian.io.SerializerFactory

因为 FstCodec 内部调用了 FST 的序列化方法,而 FST 的实现依赖于 fst-2.57.jar

  1. 替代方案

若不想使用 FST,可以切换其他序列化方式,例如:

"codec": {
    "class": "org.redisson.codec.JsonJacksonCodec"  // 使用 JSON 序列化
}

此时需引入对应的 JSON 依赖(如 jackson-databind),但性能可能不如 FST。

nginx #tomcat #session #redis

posted @ 2025-12-25 15:01  姬雨晨  阅读(4)  评论(0)    收藏  举报