快速实现 Tomcat 集群 Session 共享
前言
在应对巨大的用户流量的互联网场景中, 搭建 Tomcat 集群是缓解 Web 服务器负载的解决方式中必不可少的,而随之带来的会话信息即 Session 不同步的问题也暴露出来: 用户刚登录后,再次操作却提示需要重新登录,严重影响着用户体验. 本文主要研究如何使用 Spring Session 框架来解决 Tomcat 集群会话共享问题.若有补充,欢迎斧正.
正文
环境准备
- 3个 Tomcat 实例
- Redis
项目结构
项目比较简单,除了启动类之外,就只有一个控制器类.
控制器实现
UserController 主要有两个请求方法, 一个接受用户登录,另一个获取登录信息的;当调用 login
接口后将请求数据存在当前的 Session 中,然后在 Session 有效的期间内调用 getUserInfo
接口都能获取到对应登录时的数据.
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/login")
public String login(HttpSession session, HttpServletRequest request) {
String id = request.getParameter("id");
String name = request.getParameter("name");
HashMap<Object, Object> userInfo = new HashMap<>(16);
userInfo.put("id", id);
userInfo.put("name", name);
session.setAttribute("USER_INFO", userInfo);
return userInfo + " 成功存储到会话中";
}
@RequestMapping("/getUserInfo")
public String getUserInfo(HttpSession session, HttpServletRequest request) {
Object user_info = session.getAttribute("USER_INFO");
if (user_info == null) {
return "请先登录,再读取会话数据";
}
return "从会话中读取数据 " + user_info;
}
}
复制代码
现在我们将3个 Tomcat 实例搭建成集群,然后都运转这个项目; 如果我们针对一个 Tomcat 实例发送登录请求,然后再次发送获取用户信息请求,此时这个 Tomcat 是能够正确返回之前登录后存储的信息;而当我们在另一个 Tomcat 实例尝试获取用户信息时,则会返回 "请先登录,再读取会话数据";这说明这两个 Tomcat 实例的会话信息是独立存在的.
使用 Spring Session
现在想要让这些 Tomcat 间能够对会话信息共享,只要登录一次,就可以在其他集群实例上访问数据,就可以使用 Spring Session 框架实现,它能在对程序无任何侵入的情况 实现 Session 的共享. 首先我们要做 POM 文件引入 Spring Session 相关的库
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
复制代码
从依赖的库可以看到 Spring Session 利用内存数据库 Redis 来存储会话信息,以此达到集群间会话的共享.
引入后依赖库之后,我们就需要在 application.properties 文件上进行 Session 的配置.
server.servlet.session.timeout=3600 //1
spring.session.redis.flush-mode=IMMEDIATE //2
spring.session.redis.namespace=spring:session //3
// 4
spring.redis.host=127.0.0.1
spring.redis.password=
spring.redis.port=6380
复制代码
先简单对文件新增的配置进行简单的说明:
- 限定 Session 超时时间,默认单位为秒
- 设置 Session 刷新模式,有 ON_SAVE 和 IMMEDIATE; IMMEDIATE 表示立即写到 Redis 中;而 ON_SAVE 表示只有当执行
SessionRepository.save(org.springframework.session.Session)
时才会写入到 Redis. - 存放到 Redis 中会话的命名空间.
- 连接 Redis 数据库
然后在将项目打包到各个 Tomcat 之后再次调用登录请求,然后在 Redis 中查询下当前所有 KEYS
从图里就可以看出缓存中对 Session 数据的命名就是以前配置文件中的命名空间来的,我们取一下里面的 KEY 查看它的内容,里面就有我们所存的用户信息然后我们再对另个 Tomcat 请求获取用户信息,就可以发现返回结果不再是之前的"请先登录,再读取会话数据",而能正常返回在之前一台 Tomcat 实例上登录的会话数据信息.这也说明了 Tomcat 集群间的会话共享实现了, 是不是很简单呢?