xxl-job源码解读(一):调度器的初始化_xxl-job 默认密码

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注大数据)
img

正文

}


// ---------------------- XxlJobScheduler ----------------------

private XxlJobScheduler xxlJobScheduler;

@Override
public void afterPropertiesSet() throws Exception {
    adminConfig = this;

    xxlJobScheduler = new XxlJobScheduler();
    xxlJobScheduler.init();
}

@Override
public void destroy() throws Exception {
    xxlJobScheduler.destroy();
}
......

}


#### xxlJobScheduler.init()


init() 中有一系列的初始化方法,每个初始化方法干了什么,我已经写到上面的流程图中了基本的作用



public class XxlJobScheduler {
private static final Logger logger = LoggerFactory.getLogger(XxlJobScheduler.class);

public void init() throws Exception {
    // init i18n
    initI18n();

    // admin trigger pool start
    JobTriggerPoolHelper.toStart();

    // admin registry monitor run
    JobRegistryHelper.getInstance().start();

    // admin fail-monitor run
    JobFailMonitorHelper.getInstance().start();

    // admin lose-monitor run ( depend on JobTriggerPoolHelper )
    JobCompleteHelper.getInstance().start();

    // admin log report start
    JobLogReportHelper.getInstance().start();

    // start-schedule ( depend on JobTriggerPoolHelper )
    JobScheduleHelper.getInstance().start();

    logger.info(">>>>>>>>> init xxl-job admin success.");
}
......

}


#### JobTriggerPoolHelper.toStart()


start 方法中定义了一个快线程池和慢线程池,快线程池的最大线程数量最小为200,慢线程池的最大线程数量最小为100。  
 快慢线程池的设计思想是为了解决任务执行器并发度控制的问题,让任务执行器在任务量较大时能够快速响应,同时在任务量较小时能够节省资源。具体来说,快线程池和慢线程池的作用如下:


**快线程池**:用于执行任务量较大的任务,线程数较多,执行速度较快,能够快速响应任务。


**慢线程池**:用于执行任务量较小的任务,线程数较少,执行速度较慢,能够节省资源。



public void start(){
fastTriggerPool = new ThreadPoolExecutor(
10,
XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax(),
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue(1000),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, “xxl-job, admin JobTriggerPoolHelper-fastTriggerPool-” + r.hashCode());
}
});

slowTriggerPool = new ThreadPoolExecutor(
    10,
    XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax(),
    60L,
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<Runnable>(2000),
    new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "xxl-job, admin JobTriggerPoolHelper-slowTriggerPool-" + r.hashCode());
        }
    });

}


#### JobRegistryHelper.getInstance().start()


创建一个线程池 registryOrRemoveThreadPool 用于注册或删除任务,创建一个后台守护线程 registryMonitorThread ,使用了一个死循环每隔30秒执行一次,删除超时的注册表信息,更新xxl\_job\_group执行器地址列表。



public void start(){

// for registry or remove
registryOrRemoveThreadPool = new ThreadPoolExecutor(
2,
10,
30L,
TimeUnit.SECONDS,
new LinkedBlockingQueue(2000),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, “xxl-job, admin JobRegistryMonitorHelper-registryOrRemoveThreadPool-” + r.hashCode());
}
},
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
r.run();
logger.warn(“>>>>>>>>>>> xxl-job, registry or remove too fast, match threadpool rejected handler(run now).”);
}
});

// 创建一个守护线程 registryMonitorThread
registryMonitorThread = new Thread(new Runnable() {
@Override
public void run() {
while (!toStop) {
try {
// 查询执行器地址类型是自动注册的执行器信息表
List groupList = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().findByAddressType(0);
if (groupList!=null && !groupList.isEmpty()) {

        // 查找xxl\_job\_registry表中超时了90秒的注册信息
        List<Integer> ids = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findDead(RegistryConfig.DEAD\_TIMEOUT, new Date());
        // 如果超时 ids 集合不为空,则直接删除这些数据
        if (ids!=null && ids.size()>0) {
          XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().removeDead(ids);
        }

        HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();

        // 查询所有未过期的注册信息,将注册类型为EXECUTOR的XxlJobRegistry集合改装成appname=>设置触发器的ip地址
        List<XxlJobRegistry> list = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findAll(RegistryConfig.DEAD\_TIMEOUT, new Date());
        if (list != null) {
          for (XxlJobRegistry item: list) {
            if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) {
              String appname = item.getRegistryKey();
              List<String> registryList = appAddressMap.get(appname);
              if (registryList == null) {
                registryList = new ArrayList<String>();
              }

              if (!registryList.contains(item.getRegistryValue())) {
                registryList.add(item.getRegistryValue());
              }
              appAddressMap.put(appname, registryList);
            }
          }
        }

        // 遍历 XxlJobGroup 列表,将 appname 对应的注册器 IP 地址转化为 IP1,IP2,IP3 的形式,
        // 并更新到xxl\_job\_group表中
        for (XxlJobGroup group: groupList) {
          List<String> registryList = appAddressMap.get(group.getAppname());
          String addressListStr = null;
          if (registryList!=null && !registryList.isEmpty()) {
            Collections.sort(registryList);
            StringBuilder addressListSB = new StringBuilder();
            for (String item:registryList) {
              addressListSB.append(item).append(",");
            }
            addressListStr = addressListSB.toString();
            addressListStr = addressListStr.substring(0, addressListStr.length()-1);
          }
          group.setAddressList(addressListStr);
          group.setUpdateTime(new Date());

          XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);
        }
      }
    } catch (Exception e) {
      if (!toStop) {
        logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
      }
    }
    try {
      // 每 30 秒执行一次
      TimeUnit.SECONDS.sleep(RegistryConfig.BEAT\_TIMEOUT);
    } catch (InterruptedException e) {
      if (!toStop) {
        logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
      }
    }
  }
  logger.info(">>>>>>>>>>> xxl-job, job registry monitor thread stop");
}

});
registryMonitorThread.setDaemon(true);
registryMonitorThread.setName(“xxl-job, admin JobRegistryMonitorHelper-registryMonitorThread”);
registryMonitorThread.start();
}


#### JobFailMonitorHelper.getInstance().start()


创建一个守护线程 monitorThread ,如果失败任务设置了重试机制,则触发重试流程,设置了告警策略,则会根据告警策略触发告警操作



public void start(){
monitorThread = new Thread(new Runnable() {

    @Override
    public void run() {

        // monitor
        while (!toStop) {
            try {
                // 查询前1000条执行器执行失败且告警状态是默认的 xxl\_job\_log 表的 id 集合
                List<Long> failLogIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findFailJobLogIds(1000);
                if (failLogIds!=null && !failLogIds.isEmpty()) {
                    for (long failLogId: failLogIds) {

                        // 以乐观锁的方式将日志的告警状态设置为 -1
                        int lockRet = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateAlarmStatus(failLogId, 0, -1);
                        if (lockRet < 1) {
                            continue;
                        }
                        XxlJobLog log = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().load(failLogId);
                        XxlJobInfo info = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(log.getJobId());

                        // 1、如果设置的失败重复次数大于0,则再次执行触发器,并更新日志信息
                        if (log.getExecutorFailRetryCount() > 0) {
                            JobTriggerPoolHelper.trigger(log.getJobId(), TriggerTypeEnum.RETRY, (log.getExecutorFailRetryCount()-1), log.getExecutorShardingParam(), log.getExecutorParam(), null);
                            String retryMsg = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf\_trigger\_type\_retry") +"<<<<<<<<<<< </span><br>";
                            log.setTriggerMsg(log.getTriggerMsg() + retryMsg);
                            XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(log);
                        }

                        int newAlarmStatus = 0;		// 告警状态:0-默认、-1=锁定状态、1-无需告警、2-告警成功、3-告警失败
                        // 2、如果报警器信息不为空,则触发报警操作,并更新告警状态
                        if (info != null) {
                            boolean alarmResult = XxlJobAdminConfig.getAdminConfig().getJobAlarmer().alarm(info, log);
                            newAlarmStatus = alarmResult?2:3;
                        } else {
                            newAlarmStatus = 1;
                        }

                        XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateAlarmStatus(failLogId, -1, newAlarmStatus);
                    }
                }

            } catch (Exception e) {
                if (!toStop) {
                    logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e);
                }
            }

            try {
                // 每10ms需要执行一次
                TimeUnit.SECONDS.sleep(10);
            } catch (Exception e) {
                if (!toStop) {
                    logger.error(e.getMessage(), e);
                }
            }

        }

        logger.info(">>>>>>>>>>> xxl-job, job fail monitor thread stop");

    }
});
monitorThread.setDaemon(true);
monitorThread.setName("xxl-job, admin JobFailMonitorHelper");
monitorThread.start();

}


#### JobCompleteHelper.getInstance().start()


创建一个回调线程池 callbackThreadPool 和一个守护线程monitorThread,守护线程负责将调度记录停留在 “运行中” 状态超过10min,且对应执行器心跳注册失败不在线,则将本地调度主动标记失败;



public void start(){

// for callback
callbackThreadPool = new ThreadPoolExecutor(
        2,
        20,
        30L,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<Runnable>(3000),
        new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "xxl-job, admin JobLosedMonitorHelper-callbackThreadPool-" + r.hashCode());
            }
        },
        new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                r.run();
                logger.warn(">>>>>>>>>>> xxl-job, callback too fast, match threadpool rejected handler(run now).");
            }
        });


// for monitor
monitorThread = new Thread(new Runnable() {

    @Override
    public void run() {

        // wait for JobTriggerPoolHelper-init
        try {
            TimeUnit.MILLISECONDS.sleep(50);
        } catch (InterruptedException e) {
            if (!toStop) {
                logger.error(e.getMessage(), e);
            }
        }

        // monitor
        while (!toStop) {
            try {
                // 任务结果丢失处理:调度记录停留在 "运行中" 状态超过10min,且对应执行器心跳注册失败不在线,则将本地调度主动标记失败;
                Date losedTime = DateUtil.addMinutes(new Date(), -10);
                List<Long> losedJobIds  = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findLostJobIds(losedTime);

                if (losedJobIds!=null && losedJobIds.size()>0) {
                    for (Long logId: losedJobIds) {

                        XxlJobLog jobLog = new XxlJobLog();
                        jobLog.setId(logId);

                        jobLog.setHandleTime(new Date());
                        jobLog.setHandleCode(ReturnT.FAIL\_CODE);
                        jobLog.setHandleMsg( I18nUtil.getString("joblog\_lost\_fail") );

                        XxlJobCompleter.updateHandleInfoAndFinish(jobLog);
                    }

                }
            } catch (Exception e) {
                if (!toStop) {
                    logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e);
                }
            }

            try {
                TimeUnit.SECONDS.sleep(60);
            } catch (Exception e) {
                if (!toStop) {
                    logger.error(e.getMessage(), e);
                }
            }

        }

        logger.info(">>>>>>>>>>> xxl-job, JobLosedMonitorHelper stop");

    }
});
monitorThread.setDaemon(true);
monitorThread.setName("xxl-job, admin JobLosedMonitorHelper");
monitorThread.start();

}


#### JobLogReportHelper.getInstance().start()


每分钟刷新一次日志报告,包括当天的运行情况,包括运行次数、成功次数、失败次数等,并将这些信息保存到数据库中。  
 每天执行一次日志清理,删除指定天数前的日志数据。



public void start(){
logrThread = new Thread(new Runnable() {

    @Override
    public void run() {

        // 最近一次清理日志的时间
        long lastCleanLogTime = 0;


        while (!toStop) {

            // 1、log-report refresh: refresh log report in 3 days
            try {
                // 分别统计今天、昨天、前天 0到24点的 总调用次数、成功调用次数、失败调用次数以及正在运行的次数
                for (int i = 0; i < 3; i++) {

                    // today
                    Calendar itemDay = Calendar.getInstance();
                    itemDay.add(Calendar.DAY\_OF\_MONTH, -i);
                    itemDay.set(Calendar.HOUR\_OF\_DAY, 0);
                    itemDay.set(Calendar.MINUTE, 0);
                    itemDay.set(Calendar.SECOND, 0);
                    itemDay.set(Calendar.MILLISECOND, 0);

                    Date todayFrom = itemDay.getTime();

                    itemDay.set(Calendar.HOUR\_OF\_DAY, 23);
                    itemDay.set(Calendar.MINUTE, 59);
                    itemDay.set(Calendar.SECOND, 59);
                    itemDay.set(Calendar.MILLISECOND, 999);

                    Date todayTo = itemDay.getTime();

                    // refresh log-report every minute
                    XxlJobLogReport xxlJobLogReport = new XxlJobLogReport();
                    xxlJobLogReport.setTriggerDay(todayFrom);
                    xxlJobLogReport.setRunningCount(0);
                    xxlJobLogReport.setSucCount(0);
                    xxlJobLogReport.setFailCount(0);

                    Map<String, Object> triggerCountMap = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findLogReport(todayFrom, todayTo);
                    if (triggerCountMap!=null && triggerCountMap.size()>0) {
                        int triggerDayCount = triggerCountMap.containsKey("triggerDayCount")?Integer.valueOf(String.valueOf(triggerCountMap.get("triggerDayCount"))):0;
                        int triggerDayCountRunning = triggerCountMap.containsKey("triggerDayCountRunning")?Integer.valueOf(String.valueOf(triggerCountMap.get("triggerDayCountRunning"))):0;
                        int triggerDayCountSuc = triggerCountMap.containsKey("triggerDayCountSuc")?Integer.valueOf(String.valueOf(triggerCountMap.get("triggerDayCountSuc"))):0;
                        int triggerDayCountFail = triggerDayCount - triggerDayCountRunning - triggerDayCountSuc;

                        xxlJobLogReport.setRunningCount(triggerDayCountRunning);
                        xxlJobLogReport.setSucCount(triggerDayCountSuc);
                        xxlJobLogReport.setFailCount(triggerDayCountFail);
                    }

                    // 数据当天数据存在就更新,不存在就新建
                    int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobLogReportDao().update(xxlJobLogReport);
                    if (ret < 1) {
                        XxlJobAdminConfig.getAdminConfig().getXxlJobLogReportDao().save(xxlJobLogReport);
                    }
                }

            } catch (Exception e) {
                if (!toStop) {
                    logger.error(">>>>>>>>>>> xxl-job, job log report thread error:{}", e);
                }
            }

            // 如果设置了日志保留时间且最近24小时内没有清理过日志,则进入if语句内
            if (XxlJobAdminConfig.getAdminConfig().getLogretentiondays()>0
                    && System.currentTimeMillis() - lastCleanLogTime > 24\*60\*60\*1000) {

                // 根据日志保留时间计算日志过期的时间
                Calendar expiredDay = Calendar.getInstance();
                expiredDay.add(Calendar.DAY\_OF\_MONTH, -1 \* XxlJobAdminConfig.getAdminConfig().getLogretentiondays());
                expiredDay.set(Calendar.HOUR\_OF\_DAY, 0);
                expiredDay.set(Calendar.MINUTE, 0);
                expiredDay.set(Calendar.SECOND, 0);
                expiredDay.set(Calendar.MILLISECOND, 0);
                Date clearBeforeTime = expiredDay.getTime();

                // 清除过期的日志
                List<Long> logIds = null;
                do {
                    logIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findClearLogIds(0, 0, clearBeforeTime, 0, 1000);
                    if (logIds!=null && logIds.size()>0) {
                        XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().clearLog(logIds);
                    }
                } while (logIds!=null && logIds.size()>0);

                // 更新最近一次清除日志的时间,最近24小时不再执行清理操作
                lastCleanLogTime = System.currentTimeMillis();
            }

            try {
                // 每分钟执行一次
                TimeUnit.MINUTES.sleep(1);
            } catch (Exception e) {
                if (!toStop) {
                    logger.error(e.getMessage(), e);
                }
            }

        }

        logger.info(">>>>>>>>>>> xxl-job, job log report thread stop");

    }
});
logrThread.setDaemon(true);
logrThread.setName("xxl-job, admin JobLogReportHelper");
logrThread.start();

}


#### JobScheduleHelper.getInstance().start()


在这个类中,定义了两个线程,第一个线程是定时任务扫描处理线程,第二个线程则是一个时间轮线程。


scheduleThread线程中做的事情是将JobInfo中即将要执行的任务取出来(5秒的预读时间),然后根据三种不同的情况分别进行处理:


1. 如果当前时间大于任务触发时间+5秒,说明这个任务漏触发,根据触发漏发策略决定是否执行任务。
2. 如果当前时间大于任务的触发时间且小于触发时间+5秒,触发任务,计算下一次任务执行时间,如果下一次任务执行时间在五秒内,则放入时间轮。
3. 其他情况,任务还没到时间触发,则放入时间轮中。


ringThread线程将时间轮中的任务按秒触发。



public void start(){

    // schedule thread
    scheduleThread = new Thread(new Runnable() {
        @Override
        public void run() {

            try {
                // 使线程的执行时间对齐到整秒
                TimeUnit.MILLISECONDS.sleep(5000 - System.currentTimeMillis()%1000 );
            } catch (InterruptedException e) {
                if (!scheduleThreadToStop) {
                    logger.error(e.getMessage(), e);
                }
            }
            logger.info(">>>>>>>>> init xxl-job admin scheduler success.");
            // 计算预读的任务数,这个数量由参数控制
            // pre-read count: treadpool-size \* trigger-qps (each trigger cost 50ms, qps = 1000/50 = 20)
            int preReadCount = (XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax() + XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax()) \* 20;

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

cess.");
// 计算预读的任务数,这个数量由参数控制
// pre-read count: treadpool-size * trigger-qps (each trigger cost 50ms, qps = 1000/50 = 20)
int preReadCount = (XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax() + XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax()) * 20;

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
[外链图片转存中…(img-thVnG914-1713159637907)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值