越早知道越好(分布式session redis)分布式session是什么,你还不明白如何解决分布式Session?看这篇就够了,java session connect,
平常做的项目都是在一台应用系统,并且所有的操作都在一台Tomcat服务器上,并不会引发Session共享的问题,所以并不会对我们的系统产生影响,但是当我们部署多个微服务的时候,再搭配Nginx进行负载均衡时,如果不处理分布式Session问题,我们在系统中访问不同功能时就会频繁出现用户登录的操作。
图解分析原因:
前提:用户登录功能和图中的商品订单模块、秒杀抢购模块属于单独的微服务模块
用户登录成功后想要访问图中其他两个模块的功能时,由于Nginx使用默认负载均衡策略(轮询),这时请求会按照时间顺序逐一分发到后端应用上,也就是说用户在Tomcat1上登录成功之后,用户的信息放在Tomcat1的Session里,过了一会,用户想要进行秒杀活动的功能操作,请求又被Nginx分发到了Tomcat2,而这时的Tomcat2上的Session里还没有用户信息,于是就是出现让用户重新登录的情况,在微服务分布式项目中,不同的功能模块必然会被分列成各自的微服务,假设访问一个功能都需要重新登录一次,用户的体验必然会大幅度下降!
那如何来解决分布式Session问题呢?
一、解决方案列举
1. Session复制
优点:
无需修改代码,只需要修改Tomcat配置缺点:
Session同步传输占用内网宽带多台Tomcat同步性能指数级下降Session占用内存,无法有效水平扩展2. 前端存储
优点:
不占用服务器内存缺点:
存在安全风险数据大小受cookie限制占用外网宽带3. Session粘滞
优点:
无需修改代码服务端可以水平扩展缺点:
增加新机器,会重新Hash,导致重新登录应用重新启动后,需要重新登录4. 后端集中存储
优点:
安全容易水平扩展优点:
增加复杂度需要修改代码二、Java代码实现解决分布式Session
1. SpringSession - 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><!--spring session 依赖,公众号:Java精选--><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency>添加Redis配置
## Redis配置spring:redis:# 服务器地址host: localhost# 端口port: 6379# 数据库database: 0# 超时时间connect-timeout: 10000mslettuce:pool:# 最大连接数max-active: 8# 最大连接阻塞等待时间 默认 -1max-wait: 10000ms# 最大空闲时间 默认8max-idle: 200# 最小空闲连接 默认8min-idle: 5业务逻辑实现
/*** 登录功能* @param loginVo* @return*/@Overridepublic RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {Stringusername = loginVo.getUserName();String password = loginVo.getPassword();User user = userMapper.selectByUserName(username);if (user == null){throw newGlobalException(RespBeanEnum.LOGIN_ERROR);}//判断密码是否正确if (!MDUtils.formPassToDBPass(password,user.getSalt()).equals(user.getPassword())){throw newGlobalException(RespBeanEnum.LOGIN_ERROR);}//使用UUID生成字符串代替CookieStringticket = UUIDUtil.uuid();request.getSession().setAttribute(ticket,user);CookieUtil.setCookie(request,response,"userTicket",ticket);return RespBean.success();}视图控制层
/*** 跳转商品列表* @param session* @param model* @return*/@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>Redis配置参考 【SpringSession - Redis解决分布式Session】
业务逻辑层
@OverridepublicRespBean 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);}//成功CookieString ticket = UUIDUtil.uuid();//将用户信息存入到redis中redisTemplate.opsForValue().set("userTicket",ticket);redisTemplate.opsForValue().set("user:"+ticket,user);//request.getSession().setAttribute(ticket,user);CookieUtil.setCookie(request,response,"userTicket",ticket);return RespBean.success();}/*** 根据cookie获取cookie*@param ticket* @return*/@OverridepublicUser 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;}视图控制层
/*** 跳转商品列表* @param session* @param model* @return*/@RequestMapping("/toList")publicString toList(HttpSession session, Model model,HttpServletRequest request,HttpServletResponse response){String ticket = (String) redisTemplate.opsForValue().get("userTicket");if (StringUtils.isEmpty(ticket)){return "login";}//User user = (User) session.getAttribute(ticket);User user = userService.getUserByByCookie(ticket, request, response);if (user == null){return "login";}model.addAttribute("user",user);return "goodsList";}测试成功
查看Redis管理工具
版权申明
本文系作者 @河马 原创发布在河马博客站点。未经许可,禁止转载。
暂无评论数据