在应对巨大的用户流量的互联网场景中, 搭建 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 集群间的会话共享实现了, 是不是很简单呢?