要解決的問題
想知道每個定時任務有沒有被執行,執行的結果怎麼樣
解決思路
通過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
關鍵代碼
- 自定義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!");
}
}
}
- 加載定時任務時放入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);
}
}
- 啓動時加載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!");
}
}
}