前言
在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理。如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在A、B两台服务器。
用户第一次访问网站时,Nginx通过其负载均衡机制将用户请求转发到A服务器,这时A服务器就会给用户创建一个Session。当用户第二次发送请求时,Nginx将其负载均衡到B服务器,而这时候B服务器并不存在Session,所以就会将用户踢到登录页面。
这将大大降低用户体验度,导致用户的流失,这种情况是项目绝不应该出现的。
模拟Session丢失的情况
先配置nginx
配置nginx,为了方便就用Windows的nginx
每次访问nginx的80端口即可
模拟代码:将这个数据存到Session中
@RestController
public class MyController {
@Value("${server.port}")
int port;
//模拟登陆
@RequestMapping("/login")
public String login(HttpSession session){
session.setAttribute("name","tom");
return port+": login:"+session.getAttribute("name");
}
//模拟登录后直接从session里面获取信息
@RequestMapping("/test")
public String test(HttpSession session){
return port+": test:"+session.getAttribute("name");
}
}
复制一份springboot程序
然后准备8080和8081两个端口的服务器来进行访问
登陆,可以看到我们是从8081端口登陆,在实际情况下我们8081和8080端口部署了相同的项目,就是一个小的分布式集群,第一次访问网站时,Nginx通过其负载均衡机制将用户请求转发到8081服务器,这时8081服务器(在响应头中)就会给用户创建一个Session。如下是SessionId
但是当我们执行test方法的时候,他是进的8080端口并且name变成了null,而且SessionId变了,这是因为我们8080端口拿不到8081端口所创建的Session的信息,8080也没有执行它的login所以它的name属性就为null,并且重新给你创建了一个SessionId(可以发现两次SessionId不一样)
但是当我们在执行test方法就跳到了8081端口的test方法,这时候8081的name也为null
可以对应下面这个图进行理解,可以对应我们文章开头的前言进行理解
解决方法
我们使用Spring-Session
来进行处理,Spring-Session
实现原理就是讲session
存储到redis
中,不存在每个服务器的tomcat
中。
导入依赖
<!--Spring-Session依赖-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!--Spring-Session依赖-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
application.properties配置文件
spring.redis.host=192.168.195.157
spring.redis.port=6379
spring.redis.password=123
spring.redis.database=0
spring.redis.jedis.pool.max-active=20
spring.redis.jedis.pool.max-idle=10
server.port=${port:8080}
Controller方法
@RestController
public class MyController {
@Value("${server.port}")
int port;
@RequestMapping("/login")
public String login(HttpSession session){
session.setAttribute("name","tom");
return port+": login:"+session.getAttribute("name");
}
@RequestMapping("/test")
public String test(HttpSession session){
return port+": test:"+session.getAttribute("name");
}
}
启动类
核心就是加这个@EnableRedisHttpSession
注解
@EnableRedisHttpSession//这个是将Session信息存入redis中,也是我们解决Session丢失的关键
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
执行结果:
8081:login
8080:test
8081:test
redis也是过期时间30分钟,跟我们tomcat服务器存储session时间是一样的
我们可以清楚的看到不论是我们只需8081服务端的test方法还是8081服务端的方法SessionId均没有变化、Session里面的name信息也没有变化,这就解决了Session丢失的问题