SpringBoot学习秒杀四、分布式session

本文探讨了电商系统集群化过程中遇到的Session问题,介绍了使用Redis作为第三方缓存系统解决Session同步难题的方法,包括UUID生成、缓存过期时间设定及通过自定义参数解析器优化Controller调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个电商系统的发展,随着业务量的增加,必然是从单机系统逐渐发展为集群的,分布式的系统,而随着系统的集群化,此时也面临一个问题,就是session的问题,以往单机时代,用户的每个请求都落在同一个主机上,这样session也是在同一个主机上,但是在集群系统中,用户的请求会随机落在任意主机上,这样就会造成在一个主机中存入了session,而在另一个主机中无法取到session
在这里插入图片描述
针对这个问题,有两种比较常见的解决方案,一是实时同步各个主机的session,但是这样也会有一个问题,假设系统非常庞大,主机数量非常多,那么在各个主机之间同步session是一个非常耗费资源的行为,并且实时性很难保障,所以我们采用第二种解决方案,使用第三方的缓存系统,我们可以使用redis来做缓存,关于redis的使用,请参考:SpringBoot学习秒杀一、SpringBoot集成Redis (使用Jedis实现),这里我们使用用户登录和用户信息的获取来做演示,首先用户登录成功之后我们会获取到一个User对象,接下来我们只需要把User对象存到缓存里,也就是Redis里,使用一个token字符串做key,接下来我们需要User的信息的时候都可以从redis中取出这个User对象,此处我没有实现具体的登录数据库,而是创建了一个模拟的User用来做测试

 		User user = new User();
        user.setUserId(1);
        user.setMobile("13371081000");
        user.setPassword("123");

接下来我们需要生成一个不会重复的key,我们直接使用uuid,并且去掉uuid字符串中的“-”即可:
新建UUIDUtil:

public class UUIDUtil {
    public static String getUUID(){
        return UUID.randomUUID().toString().replace("-","");
    }
}

接下来我们新建一个前缀UserKey,我们这里设定缓存的过期时间是一天即86400s,

public class Userkey extends BasePrefix {

    public static int EXPIRE = 86400;

    private  Userkey(String prefix) {
        super(prefix);
    }
    private Userkey(int expireSeconds,String prefix){
        super(expireSeconds, prefix);
    }

    public static Userkey getBySessionId = new Userkey(EXPIRE,"session_id");

} 

接下来我们只需要在登录成功之后将User对象存入redis即可

		String uuid = UUIDUtil.getUUID();
        redisService.set(Userkey.getBySessionId,uuid,user);

接下来我们如果要使用到User对象,只需要拿着我们的token字符串去redis中取出User对象即可,我们在UserService中新建一个getByToken 方法:

public User getByToken(String token) {
        if(StringUtils.isEmpty(token)) return null;
        User user = redisService.get(Userkey.getBySessionId,token,User.class);
        redisService.set(Userkey.getBySessionId,token,user);
        return user;
    }

在上面的方法中,我在取出User对象后,又重新向Redis中设置了一下User对象,这是因为我们给Redis中存储的User设置了有效时间,但是每次我们用到User对象的时候,需要把这个有效时间重置,就像我们在登录一个网页,如果我们一天没有在进行任何操作,那么第二天我们在进行操作的时候系统会提示我们登录凭证过期,需要重新登录,但是如果我们在这一天期间内进行了操作,那么我们的过期时间就会更新到我们此时操作的时间的一天之后。

好了,通过以上操作,接下来我们每次需要User信息的时候,只需要拿着token来调用我们的getByToken方法即可。
接下来我们在进行进一步的优化,我们在每次需要使用到User的时候,都要先接收token参数,然后在调用UserService的getByToken方法,这样我们每次都要写一堆相同的代码,那么能不能我们在Controller里直接接收一个User参数,就可以进行操作了呢,这样的话我们的代码就会非常简洁了,答案是肯定的。
我们需要重写一个WebConfig,首先新建一个WebConfig,然后继承WebMvcConfigurerAdapter,我们的Controller在接收参数的时候,都会回调WebMvcConfigurerAdapter的addArgumentResolvers方法,他的形参是List argumentResolvers,因此我们只需要添加一个带有User信息的HandlerMethodArgumentResolver类即可,通过查看HandlerMethodArgumentResolver的源码我们发现,这是一个接口在这里插入图片描述
因此我们只需要新建一个类来实现这个接口即可,这个接口下有两个方法,一个是boolean supportsParameter 来限定我们的这个ArgumentResolver支持什么类的解析,另一个Object resolveArgument就是我们来获取这个类的具体实现啦

@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver {

    @Autowired
    private UserService userService;

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        Class<?> clazz = methodParameter.getParameterType();
        return clazz == User.class;
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        String token = request.getParameter("token");
        return userService.getByToken(token);
    }
}

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private UserArgumentResolver userArgumentResolver;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(userArgumentResolver);
    }
}

通过上面的一通操作,接下来我们想要获取User信息的时候,就变得非常容易了

	@GetMapping("test")
    public Result<String> test(User user){
        System.out.println(user.getMobile());
        return Result.success("success");
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值