分布式定时任务—xxl-job学习(三)——调度中心(xxl-job-admin)的启动和任务调度过程源码分析

前言

接上一篇:分布式定时任务—xxl-job学习(二)——执行器的启动过程源码分析
更早:分布式定时任务—xxl-job学习(一):简单demo搭建

在上一篇我们深度分析了下xxl-job执行器组件的启动加载原理,本篇我们来深度挖掘一下调度中心(xxl-job-admin)的启动和调度过程。

xxl.job.version使用的依旧是2.2.1-SNAPSHOT版本

一、调度中心的启动

xxl-job-admin目录层级
通过上边xxl-job-admin的代码目录层级我们可以知道它包括:controller层支持在Web页面进行CRUD、核心类core、持久层dao以及业务实现层service。

首先我们先看com.xxl.job.admin.core.conf目录下的XxlJobAdminConfig.java

1.1 分析XxlJobAdminConfig类

@Component
public class XxlJobAdminConfig implements InitializingBean, DisposableBean {
   

    private static XxlJobAdminConfig adminConfig = null;
    public static XxlJobAdminConfig getAdminConfig() {
   
        return adminConfig;
    }


    // ---------------------- 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 ----------------------

    // conf
    @Value("${xxl.job.i18n}")
    private String i18n;

    @Value("${xxl.job.accessToken}")
    private String accessToken;

    @Value("${spring.mail.from}")
    private String emailFrom;

    @Value("${xxl.job.triggerpool.fast.max}")
    private int triggerPoolFastMax;

    @Value("${xxl.job.triggerpool.slow.max}")
    private int triggerPoolSlowMax;

    @Value("${xxl.job.logretentiondays}")
    private int logretentiondays;

    // dao, service

    @Resource
    private XxlJobLogDao xxlJobLogDao;
    @Resource
    private XxlJobInfoDao xxlJobInfoDao;
    @Resource
    private XxlJobRegistryDao xxlJobRegistryDao;
    @Resource
    private XxlJobGroupDao xxlJobGroupDao;
    @Resource
    private XxlJobLogReportDao xxlJobLogReportDao;
    @Resource
    private JavaMailSender mailSender;
    @Resource
    private DataSource dataSource;
    @Resource
    private JobAlarmer jobAlarmer;


    public String getI18n() {
   
        if (!Arrays.asList("zh_CN", "zh_TC", "en").contains(i18n)) {
   
            return "zh_CN";
        }
        return i18n;
    }

    public String getAccessToken() {
   
        return accessToken;
    }

    public String getEmailFrom() {
   
        return emailFrom;
    }

    public int getTriggerPoolFastMax() {
   
        if (triggerPoolFastMax < 200) {
   
            return 200;
        }
        return triggerPoolFastMax;
    }

    public int getTriggerPoolSlowMax() {
   
        if (triggerPoolSlowMax < 100) {
   
            return 100;
        }
        return triggerPoolSlowMax;
    }

    public int getLogretentiondays() {
   
        if (logretentiondays < 7) {
   
            return -1;  // Limit greater than or equal to 7, otherwise close
        }
        return logretentiondays;
    }

    public XxlJobLogDao getXxlJobLogDao() {
   
        return xxlJobLogDao;
    }

    public XxlJobInfoDao getXxlJobInfoDao() {
   
        return xxlJobInfoDao;
    }

    public XxlJobRegistryDao getXxlJobRegistryDao() {
   
        return xxlJobRegistryDao;
    }

    public XxlJobGroupDao getXxlJobGroupDao() {
   
        return xxlJobGroupDao;
    }

    public XxlJobLogReportDao getXxlJobLogReportDao() {
   
        return xxlJobLogReportDao;
    }

    public JavaMailSender getMailSender() {
   
        return mailSender;
    }

    public DataSource getDataSource() {
   
        return dataSource;
    }

    public JobAlarmer getJobAlarmer() {
   
        return jobAlarmer;
    }

}
  • 配置了i18n(调度中心国际化配置)、accessToken(调度中心通讯TOKEN)、emailFrom(报警邮箱)、triggerPoolFastMax( 调度线程池最大线程配置)、triggerPoolSlowMax( 调度线程池最大线程配置)、logretentiondays(调度中心日志表数据保存天数)等参数;
  • 注入了DataSource、XxlJobLogDao、XxlJobInfoDao、XxlJobRegistryDao、XxlJobGroupDao、XxlJobLogReportDao、JavaMailSender、JobAlarmer等dao和service接口;
  • 同时因为它实现了InitializingBean, DisposableBean接口,所以在这个对象初始化的时候会调用afterPropertiesSet()方法。

注意:
afterPropertiesSet()方法中新建了一个XxlJobScheduler对象,并调用了它的init()方法。

1.2 分析XxlJobScheduler.init()

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

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

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

    // admin lose-monitor run
    JobLosedMonitorHelper.getInstance().start();

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

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

    // start-schedule
    JobScheduleHelper.getInstance().start();

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

1.2.1 initI18n()

// ---------------------- I18n ----------------------

private void initI18n(){
   
    for (ExecutorBlockStrategyEnum item:ExecutorBlockStrategyEnum.values()) {
   
        item.setTitle(I18nUtil.getString("jobconf_block_".concat(item.name())));
    }
}

初始化新增任务页面中的阻塞处理策略相关的国际化信息。如果没有配置国际化语言则默认为中文。

1.2.2 JobRegistryMonitorHelper.getInstance().start()

private Thread registryThread;
private volatile boolean toStop = false;
public void start(){
   
	registryThread = new Thread(new Runnable() {
   
		@Override
		public void run() {
   
			while (!toStop) {
   
				try {
   
					// auto registry group
					List<XxlJobGroup> groupList = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().findByAddressType(0);
					if (groupList!=null && !groupList.isEmpty()) {
   

						// remove dead address (admin/executor)
						List<Integer> ids = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findDead(RegistryConfig.DEAD_TIMEOUT, new Date());
						if (ids!=null && ids.size()>0) {
   
							XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().removeDead(ids);
						}

						// fresh online address (admin/executor)
						HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
						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);
								}
							}
						}

						// fresh group address
						for (XxlJobGroup group: groupList) {
   
							List<String> registryList = appAddressMap.get(group.getAppname());
							String addressListStr = null;
							if (registryList!=null && !registryList.isEmpty()) {
   
								Collections.sort(registryList);
								addressListStr = "";
								for (String item:registryList) {
   
									addressListStr += item + ",";
								}
								addressListStr = addressListStr.substring(0, addressListStr.length()-1);
							}
							group.setAddressList(addressListStr);
							XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);
						}
					}
				} catch (Exception e) {
   
					if (!toStop) {
   
						logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
					}
				}
				try {
   
					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");
		}
	});
	registryThread.setDaemon(true);
	registryThread.setName("xxl-job, admin JobRegistryMonitorHelper");
	registryThread.start();
}

分析:
该类主要是创建一个守护线程管理注册地址(每30秒执行一次)

  1. 从xxl_job_group表查询所有自动注册的执行器分组groupList
  2. 删除过期注册地址,查询xxl_job_registry表中update_time小于当前时间前90秒的记录并做delete删除;
  3. 查询xxl_job_registry表中update_time大于当前时间前90秒的注册记录,并存入HashMap<String, List<String>> appAddressMap中,key为执行器的appname,value为执行器注册地址集合;
  4. 循环groupList,从HashMap<String, List<String>> appAddressMap中获取每个分组对应的地址集合,拼接每一个地址,以","分隔,并更新到xxl_job_group中。

1.2.3 JobFailMonitorHelper.getInstance().start()

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

		@Override
		public void run() {
   

			// monitor
			while (!toStop) {
   
				try {
   

					List<Long> failLogIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findFailJobLogIds(1000);
					if (failLogIds!=null && !failLogIds.isEmpty()) {
   
						for (long failLogId: failLogIds) {
   

							// lock log
							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、fail retry monitor
							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);
							}

							// 2、fail alarm monitor
							int newAlarmStatus = 0;		// 告警状态:0-默认、-1=锁定状态、1-无需告警、2-告警成功、3-告警失败
							if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) {
   
								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 {
   
                       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();
}

分析:

  • 新建一个预警守护线程(每十秒工作一次)
    TimeUnit.SECONDS.sleep(10);

  • 从xxl_job_log表查询失败日志记录id集合,每次取1000条;
    List<Long> failLogIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findFailJobLogIds(1000);

  • 107
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RabbitsInTheGrass

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值