xxl-job v2.2.1 调度中心源码解读

调度中心

  1. 扫描加载配置 ,XxlJobAdminConfig

  2. XxlJobAdminConfig加载完成后,初始化XxlJobScheduler
    // ---------------------- 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 ----------------------
    
  3. XxlJobScheduler初始化daemon线程

    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.");
     }
    

JobRegistryMonitorHelper.getInstance().start();

初始化执行器注册,异步执行,registryThread线程,每隔30s执行一次

// step 1. 查询自动注册的执行器,对应mysql表xxl_job_group
List groupList = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().findByAddressType(0);

// step 2. 清除90s未发送心跳节点(admin/executor),对应mysql表xxl_job_registry
List ids = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findDead(RegistryConfig.DEAD_TIMEOUT, new Date());
if (ids!=null && ids.size()>0) {
  XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().removeDead(ids);
}

// step 3. 查询存活节点,同步xxl_job_registry表未注册到xxl_job_group.address_list的数据
// 3.1 数据封装
HashMap> appAddressMap = new HashMap>();
List 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 registryList = appAddressMap.get(appname);
      if (registryList == null) {
        registryList = new ArrayList();
      }

      if (!registryList.contains(item.getRegistryValue())) {
        registryList.add(item.getRegistryValue());
      }
      appAddressMap.put(appname, registryList);
    }
  }
}
// 3.2 fresh group address
for (XxlJobGroup group: groupList) {
  List 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);
}


JobFailMonitorHelper.getInstance().start(); 

监控报警,异步执行,monitorThread线程,每隔10s执行一次

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

>>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_type_retry") +"<<<<<<<<<<<
"; 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);

JobLosedMonitorHelper.getInstance().start();

超时job置为失败,异步执行,monitorThread线程,每隔60s执行一次

// 任务结果丢失处理:调度记录停留在 "运行中" 状态超过10min,且对应执行器心跳注册失败不在线,则将本地调度主动标记失败;
Date losedTime = DateUtil.addMinutes(new Date(), -10);
List 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") );

    XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateHandleInfo(jobLog);
  }

}

JobTriggerPoolHelper.toStart();

JobTriggerPoolHelper

启动快、慢线程池

// 正常线程池
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(2000),
  new ThreadFactory() {
    @Override
    public Thread newThread(Runnable r) {
      return new Thread(r, "xxl-job, admin JobTriggerPoolHelper-slowTriggerPool-" + r.hashCode());
    }
  });

JobLogReportHelper.getInstance().start();

执行记录报表,异步执行,logrThread

// 1、log-report refresh: refresh log report in 3 days
try {

  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 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);
    }

    // do refresh
    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);
  }
}

// 2、log-clean: switch open & once each day
if (XxlJobAdminConfig.getAdminConfig().getLogretentiondays()>0
    && System.currentTimeMillis() - lastCleanLogTime > 24*60*60*1000) {

  // expire-time
  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();

  // clean expired log
  List 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);

  // update clean time
  lastCleanLogTime = System.currentTimeMillis();
}

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

JobScheduleHelper.getInstance().start();

job执行

// pre-read count: treadpool-size * trigger-qps (each trigger cost 50ms, qps = 1000/50 = 20)
// 计算预读数量
int preReadCount = (XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax() + XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax()) * 20;

// 加锁
conn = XxlJobAdminConfig.getAdminConfig().getDataSource().getConnection();
connAutoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);

preparedStatement = conn.prepareStatement(  "select * from xxl_job_lock where lock_name = 'schedule_lock' for update" );
preparedStatement.execute();

 // tx start
// 1、pre read 查询运行中,到达执行时间job limit preReadCount,mysql表xxl_job_info
long nowTime = System.currentTimeMillis();
List scheduleList = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().scheduleJobQuery(nowTime + PRE_READ_MS, preReadCount);
if (scheduleList!=null && scheduleList.size()>0) {
  // 2、push time-ring
  for (XxlJobInfo jobInfo: scheduleList) {

    // time-ring jump
    if (nowTime > jobInfo.getTriggerNextTime() + PRE_READ_MS) {
      // 2.1、trigger-expire > 5s:pass && make next-trigger-time
      // 下次执行时间超过当前5s不执行,更新时间,等下一次执行
      logger.warn(">>>>>>>>>>> xxl-job, schedule misfire, jobId = " + jobInfo.getId());

      // fresh next
      refreshNextValidTime(jobInfo, new Date());

    } else if (nowTime > jobInfo.getTriggerNextTime()) {
      // 2.2、trigger-expire < 5s:direct-trigger && make next-trigger-time
            // 下次执行在5s内,执行
      // 1、trigger,加入线程池执行,快慢线程池选择策略
      JobTriggerPoolHelper.trigger(jobInfo.getId(), TriggerTypeEnum.CRON, -1, null, null, null);
      logger.debug(">>>>>>>>>>> xxl-job, schedule push trigger : jobId = " + jobInfo.getId() );

      // 2、fresh next 更新状态和下次执行时间
      refreshNextValidTime(jobInfo, new Date());

      // next-trigger-time in 5s, pre-read again
      if (jobInfo.getTriggerStatus()==1 && nowTime + PRE_READ_MS > jobInfo.getTriggerNextTime()) {

        // 1、make ring second
        int ringSecond = (int)((jobInfo.getTriggerNextTime()/1000)%60);

        // 2、push time ring
        pushTimeRing(ringSecond, jobInfo.getId());

        // 3、fresh next
        refreshNextValidTime(jobInfo, new Date(jobInfo.getTriggerNextTime()));

      }

    } else {// 执行时间 < 当前时间
      // 2.3、trigger-pre-read:time-ring trigger && make next-trigger-time

      // 1、make ring second
      int ringSecond = (int)((jobInfo.getTriggerNextTime()/1000)%60);

      // 2、push time ring
      pushTimeRing(ringSecond, jobInfo.getId());

      // 3、fresh next
      refreshNextValidTime(jobInfo, new Date(jobInfo.getTriggerNextTime()));

    }

  }

  // 3、update trigger info
  for (XxlJobInfo jobInfo: scheduleList) {
    XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().scheduleUpdate(jobInfo);
  }

} else {
  preReadSuc = false;
}

// tx stop

你可能感兴趣的:(xxl-job v2.2.1 调度中心源码解读)