一个简单算法解决集群定时任务重复执行

好久不见,今天给大家分享一个非常好用的方法,来解决集群定时任务如何避免重复执行的问题。

需求描述:

大家都知道,定时任务在我们实际项目当中是会经常被使用到的,在生产环境中,为了保证服务的高可用,所以部署的服务往往都是集群架构的。

那么这里就会带来一个问题,定时任务处理的相关的业务,其实是没有必要重复去执行的,如果重复执行反而会带来更加复杂的问题,那么我们如何来防止集群架构下,定时任务重复执行呢?

解决办法:

解决的办法有很多种,可以用分布式锁数据库锁等等,但是你们不觉得用锁来解决这个问题,有点大材小用了,而且依靠其他中间服务,如果中间服务挂了,岂不是整个定时任务都不能用了?

那么我来讲讲在 Nacos 里面是如何巧妙的解决这个问题的,在 Nacos 里面也有很多后台定时任务,而 Nacos 只用了一个简单的算法,就很巧妙的解决了这个问题。

思路如下:

首先,我们需要写一个算法,这个算法会判断当前服务是否具有执行的权力。

然后在每一个服务的定时任务执行之前,需要调用这个算法,来判断自己本身需不需要执行,如果不需要就跳过,只有判断返回为 true,才会进行真正的执行。 那么这个是怎么实现的呢?

接下来给你们看看代码就很清楚了,首先在任务真正执行的时候,会调用 responsible()方法来判断,自己需不需要执行,判断返回为 true 则执行定时具体的逻辑。

scss 复制代码
  /**
     * 执行
     *
     * @param context 上下文
     */
    @Override
    public void execute(JobExecutionContext context) {
        Crontab crontab = new Crontab();
        BeanUtils.copyProperties(context.getMergedJobDataMap().get(TaskConstants.TASK_PROPERTIES), crontab);

        try {
            // 判断自己是否需要执行任务
            if (responsible(crontab.getName())) {
                before();
                doExecute(context, crontab);
                after(crontab, null);
            }
        } catch (Exception e) {
            log.error("任务执行异常:", e);
            after(crontab, e);
        }
    }

重点就在于这个算法如何实现的,先给你们讲讲实现的逻辑:

1、首先这个算法要知道,定时任务集群的数量是多少,getServerList 这个方法我会去从 Nacos 注册中心获取定时任务健康的实例数量。

2、基于定时任务集群的数量 + 定时任务名字,这个算法的特点就是哪怕你有3台、5台定时任务的集群数量,最终,只会有一个集群实例能够获取执行权限。

arduino 复制代码
 /**
     * 责任计算,保证异步任务,集群下只有一个节点执行任务
     *
     * @param taskName
     * @return
     */
    private boolean responsible(String taskName) {

        // 获取定时任务健康实例信息
        List<String> servers = getServerList();

        // 如果没有可用的健康集群节点,则直接返回 false
        // 这里不能返回为 true,如果自身定时任务实例在执行,servers 就不可能为空,除非是出问题了
        // 如果这里直接返回为 true 就会带来重复执行的问题
        if (CollectionUtils.isEmpty(servers)) {
            return false;
        }

        // 获取当前服务ip地址
        String localAddress = getLocalAddress();
        int index = servers.indexOf(localAddress);
        int lastIndex = servers.lastIndexOf(localAddress);

        if (lastIndex < 0 || index < 0) {
            return true;
        }
		
		// 只会有一个集群实例,能返回 true
        int target = distroHash(taskName) % servers.size();
        return target >= index && target <= lastIndex;
    }

集群的数量我建议最好从注册中心动态去获取,这样哪怕其中的定时任务挂了,也能保证集群数量动态感知,从而能保证定时任务正常的被执行。

那我是如何知道这个算法的呢? 这个算法在 Nacos 源码里面就是这么用的,所以我只是把它灵活的运用到工作当中来,省去了使用分布式锁的成本。

看过 Nacos 源码的同学都知道,Nacos 底层源码读起来非常"清爽",也包括了很多巧妙的设计思想,这些都很值得你学习。

感兴趣的同学可以《点击链接》查看课程,两杯奶茶钱,换你职业一生受益的知识,又何尝而不为。

哪怕是之前从来没有阅读过源码的同学,只要跟着本小册的思路和讲解,都能完全掌握,完全不用担心!同时,本小册也会讲解我们应该如何去正确地学习源码,只有掌握了正确的方式,在源码学习过程中才不会枯燥乏味,并且能够坚持下去!

给大家看其他同学的一些评价:

相关推荐
是真的小外套1 小时前
第十五章:XXE漏洞攻防与其他漏洞全解析
后端·计算机网络·php
ybwycx3 小时前
SpringBoot下获取resources目录下文件的常用方法
java·spring boot·后端
小陈工3 小时前
Python Web开发入门(十一):RESTful API设计原则与最佳实践——让你的API既优雅又好用
开发语言·前端·人工智能·后端·python·安全·restful
小阳哥AI工具3 小时前
Seedance 2.0使用真人参考图生成视频的方法
后端
IeE1QQ3GT3 小时前
使用ASP.NET Abstractions增强ASP.NET应用程序的可测试性
后端·asp.net
Full Stack Developme4 小时前
SpringBoot多线程池配置
spring boot·后端·firefox
sxhcwgcy6 小时前
SpringBoot 使用 spring.profiles.active 来区分不同环境配置
spring boot·后端·spring
稻草猫.8 小时前
Spring事务操作全解析
java·数据库·后端·spring
希望永不加班8 小时前
SpringBoot 整合 MongoDB
java·spring boot·后端·mongodb·spring
Lzh编程小栈9 小时前
数据结构与算法之队列深度解析:循环队列+C 语言硬核实现 + 面试考点全梳理
c语言·开发语言·汇编·数据结构·后端·算法·面试