面试难题:分布式 Session 实现难点,这篇就够!

Posted androidstarjack

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试难题:分布式 Session 实现难点,这篇就够!相关的知识,希望对你有一定的参考价值。

点击上方关注 “终端研发部


 

设为“星标”,和你一起掌握更多数据库知识

来源:blog.csdn.net/Gaowumao/article/details/124309548

  • 那如何来解决分布式Session问题呢?

    • 一、解决方案列举

    • 二、Java代码实现解决分布式Session


平常做的项目都是在一台应用系统,并且所有的操作都在一台Tomcat服务器上,并不会引发Session共享的问题,所以并不会对我们的系统产生影响,但是当我们部署多个微服务的时候,再搭配nginx进行负载均衡时,如果不处理分布式Session问题,我们在系统中访问不同功能时就会频繁出现用户登录的操作 🦋 🦋

图解分析原因:

图片

前提:用户登录功能和图中的商品订单模块、秒杀抢购模块属于单独的微服务模块用户登录成功后想要访问图中其他两个模块的功能时,由于Nginx使用默认负载均衡策略(轮询 ),这时请求会按照时间顺序逐一分发到后端应用上,也就是说用户在Tomcat1上登录成功之后,用户的信息放在Tomcat1Session里,过了一会,用户想要进行秒杀活动的功能操作,请求又被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 依赖-->
<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: 10000ms
    lettuce:
      pool:
        最大连接数
        max-active: 8
        最大连接阻塞等待时间 默认 -1
        max-wait: 10000ms
        最大空闲时间 默认8
        max-idle: 200
        最小空闲连接 默认8
        min-idle: 5

业务逻辑实现

/**
     * 登录功能
     * @param loginVo
     * @return
     */
    @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);
        
        //使用UUID生成字符串代替Cookie
        String ticket = 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>

业务逻辑层

@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);
        
        //成功Cookie
        String 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
     */
    @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;
    

视图控制层

/**
     * 跳转商品列表
     * @param session
     * @param model
     * @return
     */
    @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 = (User) session.getAttribute(ticket);
        User user = userService.getUserByByCookie(ticket, request, response);
        if (user == null)
   
     
            return "login";
        
        model.addAttribute("user",user);
        return "goodsList";
    

测试成功

图片

查看Redis管理工具

图片

 


 

写在最后的话

大家看完有什么不懂的可以在下方留言讨论,也可以私信问我一般看到后我都会回复的。最后觉得文章对你有帮助的话记得点个赞哦,点点关注不迷路
@终端研发部
每天都有新鲜的干货分享!

回复 【idea激活】即可获得idea的激活方式
回复 【Java】获取java相关的视频教程和资料
回复 【SpringCloud】获取SpringCloud相关多的学习资料
回复 【python】获取全套0基础Python知识手册
回复 【2020】获取2020java相关面试题教程
回复 【加群】即可加入终端研发部相关的技术交流群
阅读更多
用 Spring 的 BeanUtils 前,建议你先了解这几个坑!

lazy-mock ,一个生成后端模拟数据的懒人工具

在华为鸿蒙 OS 上尝鲜,我的第一个“hello world”,起飞!

字节跳动一面:i++ 是线程安全的吗?

一条 SQL 引发的事故,同事直接被开除!!

太扎心!排查阿里云 ECS 的 CPU 居然达100%

一款vue编写的功能强大的swagger-ui,有点秀(附开源地址)


相信自己,没有做不到的,只有想不到的在这里获得的不仅仅是技术!



点“在看”支持小于哥呀,谢谢啦

以上是关于面试难题:分布式 Session 实现难点,这篇就够!的主要内容,如果未能解决你的问题,请参考以下文章

面试难题:分布式 Session 实现难点,这篇就够!

深入了解分布式 Session,这篇就够了!

中高级开发面试必问的Redis面试题,看这篇就够了!

你还不明白如何解决分布式Session?看这篇就够了!

Storm入门,看这篇就够了

网络面试!居然这么详细,那看这篇就够了呀!!