Spring提供了定时任务的功能,但是在多个实例的集群中,会出现定时任务重复执行多次的情况。
使用Qutaz框架自带的分布式定时任务可以很好的解决这个问题,但是讲道理功能有些过于强大,对于需求不高,乃至可以一定程度上允许失误的简单任务中,性价比比较低。
使用task任务时,可以通过在redis等缓存、数据库中创建锁来实现避免重复执行任务的功能。
基本思路如下:
- 在redis中设置一个key作为锁,值为时间戳
- 尝试用setnx方法直接设置锁,若成功则直接执行任务。
- 若设置失败,使用getset方法获取当前锁并更新时间戳
- 若获取的时间戳在有效期内,则不执行任务,否则执行任务
当然这种实现方法是很粗糙的。对于高并发的频繁定时任务处理很不完美。若执行失败也会导致任务丢失。
本方案实现的处理场景是间隔比较长的数据处理定时任务。服务器集群也只有三个节点,上线运行效果良好。
简要实现代码如下:
private boolean getLock(String key){String syncKey = "sync_lock_"+key;long curr = System.currentTimeMillis();String time = curr+"";long has = redisTemplate.setnx(syncKey,time);if(has == 1){return true;}else{String lock = redisTemplate.getSet(syncKey,time);if(lock == null){return true;}else{long l = NumberUtils.toLong(lock);//三十秒以内都算有效锁return Math.abs(curr-l)<30000;}}}
复制代码