项目需要一个定时任务,执行图片处理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
网友评论