摘要: 原创出处 blog.csdn.net/Gaowumao/article/details/124309548 「派 大 星.」欢迎转载,保留摘要,谢谢!
平常做的项目都是在一台应用系统,并且所有的操作都在一台Tomcat
服务器上,并不会引发Session共享
的问题,所以并不会对我们的系统产生影响,但是当我们部署多个微服务的时候,再搭配Nginx进行负载均衡时,如果不处理分布式Session问题,我们在系统中访问不同功能时就会频繁出现用户登录的操作 🦋 🦋
图解分析原因:

前提:用户登录功能和图中的商品订单模块、秒杀抢购模块属于单独的微服务模块
用户登录成功后想要访问图中其他两个模块的功能时,由于Nginx
使用默认负载均衡策略(轮询),这时请求会按照时间顺序逐一分发到后端应用上,也就是说用户在Tomcat1
上登录成功之后,用户的信息放在Tomcat1
的Session
里,过了一会,用户想要进行秒杀活动的功能操作,请求又被Nginx
分发到了Tomcat2
,而这时的Tomcat2
上的Session
里还没有用户信息,于是就是出现让用户重新登录的情况,在微服务分布式项目中,不同的功能模块必然会被分列成各自的微服务,假设访问一个功能都需要重新登录一次,用户的体验必然会大幅度下降!🤧🤧
那如何来解决分布式Session问题呢?
一、解决方案列举
1. Session复制
优点:
缺点:
- Session同步传输占用内网宽带
- 多台Tomcat同步性能指数级下降
- Session占用内存,无法有效水平扩展
2. 前端存储
优点:
缺点:
- 存在安全风险
- 数据大小受cookie限制
- 占用外网宽带
3. Session粘滞
优点:
缺点:
- 增加新机器,会重新Hash,导致重新登录
- 应用重新启动后,需要重新登录
4. 后端集中存储
优点:
优点:
二、Java代码实现解决分布式Session
1. SpringSession - Redis解决分布式Session
添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
|
添加Redis配置
spring: redis: 服务器地址 host: localhost 端口 port: 6379 数据库 database: 0 超时时间 connect-timeout: 10000ms lettuce: pool: 最大连接数 max-active: 8 最大连接阻塞等待时间 默认 -1 max-wait: 10000ms 最大空闲时间 默认8 max-idle: 200 最小空闲连接 默认8 min-idle: 5
|
业务逻辑实现
@Override public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
String username = loginVo.getUserName(); String password = loginVo.getPassword();
User user = userMapper.selectByUserName(username); if (user == null){ throw new GlobalException(RespBeanEnum.LOGIN_ERROR); } if (!MDUtils.formPassToDBPass(password,user.getSalt()).equals(user.getPassword())){ throw new GlobalException(RespBeanEnum.LOGIN_ERROR); } String ticket = UUIDUtil.uuid(); request.getSession().setAttribute(ticket,user); CookieUtil.setCookie(request,response,"userTicket",ticket); return RespBean.success(); }
|
视图控制层
@RequestMapping("/toList") public String toList(HttpSession session, Model model,@CookieValue("userTicket")String ticket){ if (StringUtils.isEmpty(ticket)){ return "login"; } User user = (User) session.getAttribute(ticket); if (user == null){ return "login"; } model.addAttribute("user",user); return "goodsList"; }
|
登录测试

打开Redis管理软件发现Session信息已经添加到Redis中了

2. Redis解决分布式Session
导入依赖
<!--Redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--commons-pools2 对象池依赖--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
|
业务逻辑层
@Override public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
String username = loginVo.getUserName(); String password = loginVo.getPassword();
User user = userMapper.selectByUserName(username); if (user == null){ throw new GlobalException(RespBeanEnum.LOGIN_ERROR); } if (!MDUtils.formPassToDBPass(password,user.getSalt()).equals(user.getPassword())){ throw new GlobalException(RespBeanEnum.LOGIN_ERROR); } String ticket = UUIDUtil.uuid(); redisTemplate.opsForValue().set("userTicket",ticket); redisTemplate.opsForValue().set("user:"+ticket,user); CookieUtil.setCookie(request,response,"userTicket",ticket); return RespBean.success(); }
@Override public User getUserByByCookie(String ticket,HttpServletRequest request,HttpServletResponse response) { if (StringUtils.isEmpty(ticket)){ return null; } User user = (User) redisTemplate.opsForValue().get("user:" + ticket); if (user == null){ CookieUtil.setCookie(request,response,"userTicket",ticket); } return user; }
|
视图控制层
@RequestMapping("/toList") public String toList(HttpSession session, Model model,HttpServletRequest request,HttpServletResponse response){ String ticket = (String) redisTemplate.opsForValue().get("userTicket"); if (StringUtils.isEmpty(ticket)){ return "login"; } User user = userService.getUserByByCookie(ticket, request, response); if (user == null){ return "login"; } model.addAttribute("user",user); return "goodsList"; }
|
测试成功

查看Redis管理工具
