美文网首页
Redis解决,多实例执行work,同步锁问题

Redis解决,多实例执行work,同步锁问题

作者: 未名枯草 | 来源:发表于2018-02-12 15:08 被阅读140次

    项目需要一个定时任务,执行图片处理job,并上传云存储,部署多台docker,避免多个docker中的work同步执行,防止重复实现数据重复上传存储,需要设计锁机制:
    此时:需要注意锁设定是否成功问题;死锁问题;抢锁问题等,
    直接上代码分析:

    private Integer J_TIMEOUT;
    
        @Scheduled(cron = "0 0/9 * * * ?") //测试,使用9分钟执行一次。  
    
        public void HandleSchedule(){
            String today= TimeUtil.getNowDayDate(new Date());
            String time_Lock="time_Key_today";
            String sync_Lock= "sync_Lock";
    
            //时间标记判断
            if (redisUtils.exists(time_Lock)&&today.equals(redisUtils.get(time_Lock))) {
                log.info("Today the Job had Handled! today:" + today);
                return;
            }
            //lock锁判断
            if (redisUtils.exists(sync_Lock)&&today.equals(redisUtils.get(sync_Lock))){
                log.info("sync  Today the Job is Handling by Other Machine,Locked! today:"+ today);
                return;
            }
            //避免死锁,如果过期时间未生效/未成功设置情况下,删除锁
            if (redisUtils.exists(sync_Lock)&&!(today.equals(redisUtils.get(sync_Lock)))){
                log.info("sync LockKey Value is not Today! Delete the Key! today:"+ today);
                redisUtils.del(sync_Lock);
            }
    
            Boolean existSuccessLock= redisUtils.setnx(sync_Lock,today); //设置锁lock  setnx();
            if (!existSuccessLock){
                log.info("sync LockKey set False! Today:"+ today);
                return;
            }
            Long successExpire = 0L;
            try {
                successExpire = redisUtils.expire(sync_Lock,J_TIMEOUT); //设置锁lock过期时间
                if (successExpire.equals(0L)) {
                    log.info("Set sync Lock Timeout False! today:"+today);
                    return;
                }
                //设置锁lock过期时间 成功
                log.info("Having the Redis sync Lock! sync_Lock:"+sync_Lock);
                boolean workSuccessTab =true;
                workSuccessTab = HandleTask2.work();
                if (workSuccessTab){
                    redisUtils.setnx(time_Lock,today); //标记当前任务执行完成,此处要添加失败重试
                }
                log.info("******One Job Schedule End!******");
    
            }catch(Exception e) {
                log.error(" Exception!e:"+e);
            }finally {
                log.info("Delete sync Lock! Today:"+today);
                redisUtils.del(lockKey);
            }
        }
    
    
    1. 判断时间锁time_Lock的值是否是当天today,如果是,表明已经有work执行了job,今天其他work不能继续执行job;
    2. 如果time_Lock不是今天,表明当天还没执行过,可执行下一步;
    3. 首先判断同步锁sync_Lock的值value是否存在,如果存在,再判断是否是今天,如果是今天,说明锁被其他实例占用个,此时work停止继续执行,
    4. 如果同步锁sync_Lock的值value是否存在,如果存在,value不是今天,则说明是死锁,此时del这个锁,开始执行第6步
    5. 若果不存在这个锁,则同样执行第6步;
    6. 获取锁,如果获取成功,则继续执行,如果获取失败,则退出当前这次执行;
    7. 设定过期时间,如果设定成功则执行下一步,如果设定时间失败,则直接删除获取的sync_Lock锁;如果在设定过期时间之前机器宕机,此时锁已经获取并设定值,此时形成死锁,通过第4步解决。
    8. 获取了锁并设定成功了过期时间,则执行任务;
    9. 如果执行任务成功,则设定time_Lock;
    10.不过结果如何,最后finally要删除同步锁,防止过期时间失效引起死锁。

    流程图如图:

    多实例执行任务同步锁流程图.png

    相关文章

      网友评论

          本文标题:Redis解决,多实例执行work,同步锁问题

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