利用redis 分布式锁 解决集群环境下多次定时任务执行
定时任务:
@Scheduled(cron= "0 39 3 * * *") public void getAllUnSignData(){ //检查任务锁,若其它节点的相同定时任务已经执行,则该节点的任务执行一个空任务,否则设置锁并执行该任务 String timerName = this.getClass().getName()+Thread.currentThread() .getStackTrace()[1].getMethodName();//当前类名+当前方法名 if(redisLock.requireLock(timerName,7200)){ return; } long startTime = System.currentTimeMillis(); logger.info("定时任务 UnSignStatistics start-->" + startTime ); //找出签到异常的学生,保存在表中 gxyWarnService.saveUnSignWarn(); long endTime = System.currentTimeMillis(); logger.info("定时任务 UnSignStatistics end-->" +endTime+ ", execute time: " + (endTime - startTime) + "ms" ); }
用类名+方法名作为key, 去redis 获取锁。
如果获取到了键值对: (key , 1) ,则说明定时任务已被执行,返回true , 执行if 中的return, 不执行 后面的定时任务。
如果没有获取到 键值对: (key , 1) ,则说明定时任务没有被执行 。在redis中 设置键值对 (key , 1), (并设置有效期7200秒,这个有效期小于定时任务的周期即可),并返回false 。不执行if 中的return,继续执行后面的定时任务代码。
@Component public class RedisLock { //redis中存任务锁的key前缀 public final static String MOGUDING_TIMER_LOCK_KEY = "com:lock:job:"; @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 判断是否有锁。有,返回true;否,返回false,设置一定期效的锁 * @param lockName * @param expire 锁的有效时间长,单位:秒 * @return */ public boolean requireLock(String lockName,long expire){ String key = MOGUDING_TIMER_LOCK_KEY+lockName; if(redisTemplate.opsForValue().getAndSet(key,"1")==null){ //“1”这里无任何意义 redisTemplate.expire(key,expire,TimeUnit.SECONDS); return false; }else { return true; } } /** * 判断是否有锁。有,返回true;否,返回false,设置一定期效的锁 * @param lockName * @param date 锁的有效时间截止点 * @return */ public boolean requireLock(String lockName,Date date){ String key = MOGUDING_TIMER_LOCK_KEY+lockName; if(redisTemplate.opsForValue().getAndSet(key,"1")==null){ //“1”这里无任何意义 redisTemplate.expireAt(key,date); return false; }else { return true; } } }