在应对巨大的用户流量的互联网场景中, 搭建 Tomcat 集群是缓解 Web 服务器负载的解决方式中必不可少的,而随之带来的会话信息即 Session 不同步的问题也暴露出来: 用户刚登录后,再次操作却提示需要重新登录,严重影响着用户体验. 本文主要研究如何使用 Spring Session 框架来解决 Tomcat 集群会话共享问题.若有补充,欢迎斧正.
项目比较简单,除了启动类之外,就只有一个控制器类.
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 实例的会话信息是独立存在的.
现在想要让这些 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 复制代码
先简单对文件新增的配置进行简单的说明:
SessionRepository.save(org.springframework.session.Session)
然后在将项目打包到各个 Tomcat 之后再次调用登录请求,然后在 Redis 中查询下当前所有 KEYS
从图里就可以看出缓存中对 Session 数据的命名就是以前配置文件中的命名空间来的,我们取一下里面的 KEY 查看它的内容,里面就有我们所存的用户信息
然后我们再对另个 Tomcat 请求获取用户信息,就可以发现返回结果不再是之前的"请先登录,再读取会话数据",而能正常返回在之前一台 Tomcat 实例上登录的会话数据信息.这也说明了 Tomcat 集群间的会话共享实现了, 是不是很简单呢?