分布式Session

为什么使用分布式Session

单体项目一般使用的是一台Tomcat服务器,当用户量多的时候会将系统部署到多台服务器上会使用到nginx作为代理,就会出现用户登录的问题,

原因:Nginx默认使用的是轮询的负载均衡策略,请求会按照一定的时间顺序发送到后端服务器上,比如刚开始使用的是Tomcat1服务器做的登录操作,此时用户登录的Session信息就会存放在Tomcat1服务器中,当登录成功后,切换到其他页面发起请求时,此时请求被Nginx分发到了Tomcat2服务器上,这是Tomcat2服务器上的Session中没有这个用户的登录信息,导致用户又要重新登录。用户体验不好。
在这里插入图片描述

解决方案

引入了分布式Session

  • Session复制
    优点
    无需修改代码,只需要修改Tomcat配置
    缺点
    Session同步传输占用内网带宽
    多台Tomcat同步性能指数级下降
    Session占用内存,无法有效水平扩展
  • 前端存储
    优点
    不占用服务端内存
    缺点
    存在安全风险
    数据大小受cookie限制
    占用外网带宽
  • Session粘滞
    优点
    无需修改代码
    服务端可以水平扩展
    缺点
    增加新机器,会重新Hash,导致重新登录
    应用重启,需要重新登录
  • 后端集中存储
    优点
    安全
    容易水平扩展
    缺点
    增加复杂度
    需要修改代码

Redis实现分布式Session

方法一:使用SpringSession实现

  1. 添加依赖
<!-- spring data redis 依赖 -->
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- commons-pool2 对象池依赖 -->
        <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>

2.添加配置

spring:
  redis:
    #超时时间
    timeout: 10000ms
    #服务器地址
    host: 127.0.0.1
    #服务器端口
    port: 6379
    #数据库
    database: 0
    lettuce:
      pool:
        #最大连接数,默认8
        max-active: 1024
        #最大连接阻塞等待时间,默认-1
        max-wait: 10000ms
        #最大空闲连接
        max-idle: 200
        #最小空闲连接
        min-idle: 5

3.测试
在这里插入图片描述

方法二:将用户信息存入Redis

1.依赖

		<!-- spring data redis 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- commons-pool2 对象池依赖 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

2.添加配置

spring:
  redis:
    #超时时间
    timeout: 10000ms
    #服务器地址
    host: 127.0.0.1
    #服务器端口
    port: 6379
    #数据库
    database: 0
    lettuce:
      pool:
        #最大连接数,默认8
        max-active: 1024
        #最大连接阻塞等待时间,默认-1
        max-wait: 10000ms
        #最大空闲连接
        max-idle: 200
        #最小空闲连接
        min-idle: 5

3.添加Redis配置类


/**
 * Redis的配置,主要完成Redis的序列化
 * @author JiaShuai
 * @date 2022/10/9 16:04
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        //key序列器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //value序列器
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        //Hash类型 key序列器
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        //Hash类型 value序列器
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
}

此时会有一个问题,每次都要查询判断信息,所以进行优化,在用户信息到达Controller层之前进行判断,将判断好的数据传入,


/**
 * @author JiaShuai
 * @date 2022/10/9 16:34
 */
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
    @Autowired
    private UserService userService;
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        Class<?> clazz = parameter.getParameterType();
        return clazz == User.class;
    }
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        String ticket = CookieUtil.getCookieValue(request, "userTicket");
        if (StringUtils.isEmpty(ticket)) {
            return null;
        }
        return userService.getUserByCookie(ticket, request, response);
    }

}

/**
 * @author JiaShuai
 * @date 2022/10/9 16:36
 */
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private UserArgumentResolver userArgumentResolver;
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(userArgumentResolver);
    }
}

(1)Controller层

/**
 * @author JiaShuai
 * @date 2022/10/9 14:53
 */
@Controller
@RequestMapping("/goods")
public class GoodsController {

    @Autowired
    private UserService userService;

    /*跳转到商品列表页*/
    @RequestMapping("/toList")
    public String toList(Model model, User user){
//        if (StringUtils.isEmpty(ticket)){
//            return "login";
//        }
        User user=(User) session.getAttribute(ticket);
//        User user = userService.getUserByCookie(ticket,request,response);
//        if (null==user){
//            return "login";
//        }
        model.addAttribute("user",user);
        return "goodsList";
    }
}

(2)Service层

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author jishuai
 * @since 2022-10-08
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    /*登录功能*/
    @Override
    public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
        String mobile = loginVo.getMobile();
        String password = loginVo.getPassword();
//        if (StringUtils.isEmpty(mobile)||StringUtils.isEmpty(password)){
//            return RespBean.error(RespBeanEnum.LOGINVO_ERROR);
//        }
//        if (!ValidatorUtil.isMobile(mobile)){
//            return RespBean.error(RespBeanEnum.MOBILE_ERROR);
//        }
        //根据手机号获取用户信息
        User user = userMapper.selectById(mobile);
        if (null==user){
            throw new GlobalException(RespBeanEnum.LOGINVO_ERROR);
        }
        //判断密码是否正确
        if (!MD5Util.formPassToDBPass(password,user.getSlat()).equals(user.getPasword())){
            throw new GlobalException(RespBeanEnum.LOGINVO_ERROR);
        }
        //生成Cookie
        String ticket= UUIDUtil.uuid();
        //存放用户信息到session中
//        request.getSession().setAttribute(ticket,user);
        //将用户信息存放到Redis中
        redisTemplate.opsForValue().set("user:"+ticket,user);
        CookieUtil.setCookie(request,response,"userTicket",ticket);
        return RespBean.success();
    }

    /*根据Cookie获取用户*/
    @Override
    public User getUserByCookie(String userTicket,HttpServletRequest request,HttpServletResponse response) {
        if (StringUtils.isEmpty(userTicket)){
            return null;
        }
        User user=(User) redisTemplate.opsForValue().get("user:"+userTicket);
        if (user!=null){
            CookieUtil.setCookie(request,response,"userTicket",userTicket);
        }
        return user;
    }
}

在这里插入图片描述
总结:将用户信息存放在Redis中在Nginx分发时现在Redis中查询用户信息,再在Tomcat中处理。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小唐僧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值