美文网首页java
Spring Schedule+Redisson分布式锁构建分布

Spring Schedule+Redisson分布式锁构建分布

作者: 二妹是只猫 | 来源:发表于2019-03-13 15:11 被阅读0次
    先看看原生如何实现分布式锁:
    • 在applicationContext中添加:
     <context:property-placeholder location="classpath:datasource.properties"/>
     <task:annotation-driven/>
    
    • 新建一个TaskSchedule类用于创建定时任务:
    @Component
    @Slf4j
    public class TaskSchedule {
        @PreDestroy
        public void delLock(){
            RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
        }
        @Scheduled(cron="0 */1 * * * ?")
        public void taskV1(){
            log.info("关闭订单定时任务启动");
            long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000"));
            Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
            if(setnxResult!=null&&setnxResult==1){
                //执行任务逻辑
                close(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            }else{
                log.info("没有获得分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            }
            log.info("关闭订单定时任务结束");
        }
        @Scheduled(cron="0 */1 * * * ?")
        public void taskV2(){
            log.info("关闭订单定时任务启动");
            long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000"));
            Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
            if(setnxResult != null && setnxResult.intValue() == 1){
                closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            }else{
                //未获取到锁,继续判断,判断时间戳,看是否可以重置并获取到锁
                String lockValueStr = RedisShardedPoolUtil.get(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
                if(lockValueStr != null && System.currentTimeMillis() > Long.parseLong(lockValueStr)){
                    String getSetResult = RedisShardedPoolUtil.getSet(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
                    //再次用当前时间戳getset。
                    //返回给定的key的旧值,->旧值判断,是否可以获取锁
                    //当key没有旧值时,即key不存在时,返回nil ->获取锁
                    //这里我们set了一个新的value值,获取旧的值。
                    if(getSetResult == null || (getSetResult != null && StringUtils.equals(lockValueStr,getSetResult))){
                        //真正获取到锁
                        close(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
                    }else{
                        log.info("没有获取到分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
                    }
                }else{
                    log.info("没有获取到分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
                }
            }
            log.info("关闭订单定时任务结束");
        }
        private void close(String lockName){
            RedisShardedPoolUtil.expire(lockName,50);//有效期50秒,防止死锁
            log.info("获取{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
            此处开始执行真正逻辑
            。。。
            执行完毕
            RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            log.info("释放{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
            log.info("===============================");
        }
    }
    
    • 使用Scheduled注解标记需要定时的方法,Cron表达式设置执行时间。
    taskV1
    • taskV1中通过setnx判断是存储过锁(setnx方法只能添加新的键值对,已存在key执行该方法会失败)。
    • close()中先设置redis的过期时间防止死锁,在执行真正需要的逻辑,最后删除锁
    • 添加注解@PreDestroy,当tomcat通过shutdown命令关闭会执行该注解。
      但taskV1仍然有死锁的可能,所以出现taskV2方法双重防死锁

    taskV2
    • 前面的步骤与v1相同,但在设置锁失败的情况下,先去获取锁的过期时间值得到lockValueStr,然后判断获取到锁的是否过期。
    • 通过redis的getSet方法重新设置过期时间(getSet如果之间不存在该key,设置并返回当前value,反之返回nil也就时null)
    • 通过getSetResult判断执行close方法
    最后在两端的tomcat启动该定时任务,原生实现完成

    Spring Schedule+Redisson方案
    • 在pom中引入依赖:
        <dependency>
          <groupId>org.redisson</groupId>
          <artifactId>redisson</artifactId>
          <version>2.9.0</version>
        </dependency>
        <dependency>
          <groupId>com.fasterxml.jackson.dataformat</groupId>
          <artifactId>jackson-dataformat-avro</artifactId>
          <version>2.9.0</version>
        </dependency>
    
    • 创建RedissonManager用于redisson初始化:
    @Component
    @Slf4j
    public class RedissonManager {
    
        private Config config = new Config();
    
        private Redisson redisson = null;
    
        public Redisson getRedisson() {
            return redisson;
        }
    
        private static String redis1Ip = PropertiesUtil.getProperty("redis1.ip");
        private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));
        private static String redis2Ip = PropertiesUtil.getProperty("redis2.ip");
        private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));
    
        @PostConstruct
        private void init(){
            try {
                config.useSingleServer().setAddress(new StringBuilder().append(redis1Ip).append(":").append(redis1Port).toString());
    
                redisson = (Redisson) Redisson.create(config);
    
                log.info("初始化Redisson结束");
            } catch (Exception e) {
                log.error("redisson init error",e);
            }
        }
    }
    

    在TaskSchedule中添加redisson方案:

        @Scheduled(cron="0 */1 * * * ?")
        public void closeOrderTaskV4(){
            RLock lock = redissonManager.getRedisson().getLock(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            boolean getLock = false;
            try {
                if(getLock = lock.tryLock(0,50, TimeUnit.SECONDS)){
                    log.info("Redisson获取到分布式锁:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
                    int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
                    close();            
                }else{
                    log.info("Redisson没有获取到分布式锁:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                log.error("Redisson分布式锁获取异常",e);
            } finally {
                if(!getLock){
                    return;
                }
                lock.unlock();
                log.info("Redisson分布式锁释放锁");
            }
        }
    

    到此完成,redisson的对于分布式锁的构建极大的降低我们上手的难度。

    相关文章

      网友评论

        本文标题:Spring Schedule+Redisson分布式锁构建分布

        本文链接:https://www.haomeiwen.com/subject/vtozpqtx.html