A.11 springboot 互斥定时任务

背景

当我们的项目为了达到负载均衡或者备份的目的,服务通常会启动两个及以上的实例,但是如果服务中有定时任务,定时任务又不需要启动多个,只需要单实例运行,因此造成了矛盾。本主要讲解水平扩展后的服务,如何做定时任务的单实例互斥运行

环境

  • springboot:在springboot环境下
  • redis:配合redis进行

思路

  1. 利用spring的@Scheduled机制在项目中开启定时任务
  2. springboot集成redis后得到StringRedisTemplate
  3. 在定时任务执行前获取redis的锁
  4. 判断是否获取到锁?
  5. 如果获取到锁,设置锁的有效期(防止程序意外停止),执行任务,最后释放锁
  6. 如果未获取到锁,表明锁已经被其他的服务实例抢走,实例放弃定时任务

关键代码如下

/**
 * 分布式定时任务的测试
 * @author mateng
 */
@Component
public class SchedueService {
    
    private static final Logger logger = LoggerFactory.getLogger(SchedueService.class);
    
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    
    private final String key = "job";
    private final String value = "running";
    
    @Scheduled(fixedRate=2000)
    public void testaaa() {
        boolean lock = false;
        try {
            // 获取锁
            lock = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
            logger.debug("是否获取到锁:" + lock);
            if (lock) {
                // 如果在执行任务的过程中,程序突然挂了,为了避免程序因为中断而造成一直加锁的情况产生,20分钟后,key值失效,自动释放锁,
                stringRedisTemplate.expire(key, 20, TimeUnit.MINUTES);
                
                //执行定时任务
                doSomething();
            } else {
                logger.debug("没有获取到锁,不执行任务!");
                return;
            }
        } finally {// 无论如何,最终都要释放锁
            if (lock) {// 如果获取了锁,则释放锁
                stringRedisTemplate.delete(key);
                logger.debug("任务结束,释放锁!");
            } else {
                logger.debug("没有获取到锁,无需释放锁!");
            }
        }
    }

    private void doSomething() {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

展望

后期章节中将会讲解真正的分布式定时任务的实现,将一个定时任务拆解到多个服务中进行同时的定时任务执行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容