6.Quartz應用場景之定時任務監控

要解決的問題

想知道每個定時任務有沒有被執行,執行的結果怎麼樣

解決思路

通過JobListener實現定時任務歷史記錄

額外庫表

#增加STATUS字段記錄定時任務執行狀態
CREATE TABLE `my_job_details` (
  `ID` int unsigned NOT NULL AUTO_INCREMENT,
  `JOB_NAME` varchar(190) NOT NULL,
  `JOB_GROUP` varchar(190) NOT NULL,
  `DESCRIPTION` varchar(250) DEFAULT NULL,
  `JOB_CLASS_NAME` varchar(250) NOT NULL,
  `CRON_EXPRESSION` varchar(120) NOT NULL,
  `IS_DURABLE` varchar(1) NOT NULL,
  `IS_NONCONCURRENT` varchar(1) NOT NULL,
  `IS_UPDATE_DATA` varchar(1) NOT NULL,
  `REQUESTS_RECOVERY` varchar(1) NOT NULL,
  `JOB_DATA` blob,
  `STATUS` varchar(20) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
#記錄定時任務執行歷史
CREATE TABLE `my_job_exec_history` (
  `ID` int unsigned NOT NULL AUTO_INCREMENT,
  `JOB_DETAILS_ID` int NOT NULL,
  `SERVER_KEY` varchar(200) NOT NULL,
  `BEGIN_TIME` datetime NOT NULL,
  `END_TIME` datetime NOT NULL,
  `IS_MANUAL` varchar(1) NOT NULL,
  `STATUS` varchar(20) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8

關鍵代碼

  1. 自定義Listener
@Service
public class JobStatusUpdateListener extends JobListenerSupport {
    @Autowired
    MyJobDetailService myJobDetailService;
    @Autowired
    MyJobExecHistoryService myJobExecHistoryService;
    @Override
    public String getName() {
        return "JobStatusUpdateListener";
    }
    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        try {
            JobKey jobKey = context.getJobDetail().getKey();
            String jobName = jobKey.getName();
            String jobGroup = jobKey.getGroup();
            myJobDetailService.updateJobStatus(jobName,jobGroup, JobStatus.EXECUTING);
            //快要執行時把開始時間傳入JobDataMap
            JobDataMap dataMap = context.getMergedJobDataMap();
            dataMap.put(JobDataMapKey.BEGIN_TIME,Long.valueOf(System.currentTimeMillis()));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("jobExecutionVetoed failed in job listerner!");
        }
    }
    @Override
    public void jobWasExecuted(JobExecutionContext context,
                               JobExecutionException jobException) {
        try {
            JobKey jobKey = context.getJobDetail().getKey();
            String jobName = jobKey.getName();
            String jobGroup = jobKey.getGroup();
            String status = JobStatus.SUCCESS;
            if(jobException != null){
                status = JobStatus.FAILED;
            }
            myJobDetailService.updateJobStatus(jobName,jobGroup, status);
            //從JobDataMap中取出開始時間
            JobDataMap dataMap = context.getMergedJobDataMap();
            long beginTime = dataMap.getLong(JobDataMapKey.BEGIN_TIME);
            MyJobExecHistory myJobExecHistory = new MyJobExecHistory();
            //從JobDataMap中取出JOB_DETAILS_ID,以便歷史表與定時任務定義表關聯
            myJobExecHistory.setJobDetailId(dataMap.getLongValue(JobDataMapKey.MY_JD_ID));
            myJobExecHistory.setBeginTime(new Date(beginTime));
            myJobExecHistory.setEndTime(new Date());
            myJobExecHistory.setIsManual("N");
            myJobExecHistory.setServerKey(ServerInfoUtil.getServerKey());
            myJobExecHistory.setStatus(status);
            //插入歷史表
            myJobExecHistoryService.insert(myJobExecHistory);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("jobWasExecuted failed in job listerner!");
        }
    }
}
  1. 加載定時任務時放入JOB_DETAILS_ID
@Service
public class MyJobDetailService {
    private void schduleJob(MyJobDetail myJobDetail) throws ClassNotFoundException, SchedulerException {
        //CherryJobDetail ID
        Long jobId = myJobDetail.getId();
        //任務名稱
        String name = myJobDetail.getJobName();
        //任務所屬分組
        String group = myJobDetail.getJobGroup();
        //正在執行動作的類
        String jobClassName = myJobDetail.getJobClassName();
        //創建任務,將JOB_DETAILS_ID放入JobDataMap
        JobDetail jobDetail = null;
        jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(jobClassName)).withIdentity(name, group).usingJobData(JobDataMapKey.MY_JD_ID,jobId).build();
        //創建任務觸發器
        String cronExpression = cherryJobDetail.getCronExpression();
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group).withSchedule(scheduleBuilder).build();
        //將觸發器與任務綁定到調度器內
        scheduler.scheduleJob(jobDetail, trigger);
    }
}
  1. 啓動時加載Listener
@Component
public class JobLoadAndMonitorRunner implements ApplicationRunner {
    @Autowired
    MyJobLoadCtrlService myJobLoadCtrlService;
    @Autowired
    MyJobDetailService myJobDetailService;
    @Autowired
    JobStatusUpdateListener jobStatusUpdateListener;
    @Autowired
    Scheduler scheduler;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        loadJobToScheduler();
        registerJobListener();
    }

    /**
     * 加載JobDetail數據到Scheduler中(同時會初始化到Quartz表中)
     */
    private void loadJobToScheduler(){
        boolean gotOneSeat = myJobLoadCtrlService.holdingOneSeat();
        //若佔到座位,即其他服務器還沒開始加載
        String serverKey = ServerInfoUtil.getServerKey();
        if (gotOneSeat) {
            System.out.println("##################"+serverKey+" got one seat!");
            String loadStatus = myJobDetailService.loadAllJobDetail();
            System.out.println("##################MyJobDetails load:" + loadStatus);
        }else{
            System.out.println("##################"+serverKey+" doesn't got one seat!");
        }
    }

    /**
     * 註冊Job監聽器到Scheduler
     */
    public void registerJobListener(){
        try {
            scheduler.getListenerManager().addJobListener(jobStatusUpdateListener, allJobs());
        } catch (SchedulerException e) {
            e.printStackTrace();
            System.out.println("################## register jobListeners failed!");
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章