1、问题及解决
为了测试quartz任务调度是否好使,我在数据库表中添加了一个测试任务com.***.tasks.service.MyTask1。发现可以正常调度,然后我就讲数据中的配置给删除了,这是后再启动的时候就会报错,如下:
2018-08-23 16:49:24.430 [QuartzScheduler_schedulerFactoryBean-NON_CLUSTERED_MisfireHandler] ERROR org.quartz.impl.jdbcjobstore.JobStoreTX - MisfireHandler: Error handling misfires: Couldn't store trigger 'testTaskGrp.com.***.tasks.service.MyTask1' for 'testTaskGrp.com.***.tasks.service.MyTask1' job:com.***.tasks.service.MyTask1
org.quartz.JobPersistenceException: Couldn't store trigger 'testTaskGrp.com.***.tasks.service.MyTask1' for 'testTaskGrp.com.***.tasks.service.MyTask1' job:com.***.tasks.service.MyTask1
at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1222)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.doUpdateOfMisfiredTrigger(JobStoreSupport.java:1036)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.recoverMisfiredJobs(JobStoreSupport.java:985)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.doRecoverMisfires(JobStoreSupport.java:3155)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$MisfireHandler.manage(JobStoreSupport.java:3913)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$MisfireHandler.run(JobStoreSupport.java:3934)
Caused by: java.lang.ClassNotFoundException: com.***.tasks.service.MyTask1
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.springframework.scheduling.quartz.ResourceLoaderClassLoadHelper.loadClass(ResourceLoaderClassLoadHelper.java:76)
at org.springframework.scheduling.quartz.ResourceLoaderClassLoadHelper.loadClass(ResourceLoaderClassLoadHelper.java:81)
at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.selectJobDetail(StdJDBCDelegate.java:852)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1204)
... 5 common frames omitted
开始以为是代码缓存的事情,把本地缓存全部清除,发现依然不好使,然后想还是看看代码,发现如果任务不在使用的时候需要从Scheduler对象中删除,因为任务发布到它的数据表中了。然后从quartz任务表中删除所有与测试任务有关的信息就不再出现这个问题了。
2、quartz持久化实现
A、配置
#集群配置
org.quartz.scheduler.skipUpdateCheck=true
org.quartz.scheduler.instanceName=DefaultQuartzScheduler
org.quartz.scheduler.rmi.export=false
org.quartz.scheduler.rmi.proxy=false
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=10
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
org.quartz.jobStore.misfireThreshold=60000
#============================================================================
# Configure JobStore
#============================================================================
#默认配置,数据保存到内存
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
#持久化配置
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.misfireThreshold=60000
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.dataSource=quartzDataSource
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.clusterCheckinInterval=15000
org.quartz.dataSource.quartzDataSource.driver=oracle.jdbc.OracleDriver
org.quartz.dataSource.quartzDataSource.URL=jdbc:oracle:thin:@//localhost:1521/orcl
org.quartz.dataSource.quartzDataSource.user=***
org.quartz.dataSource.quartzDataSource.password=***
org.quartz.dataSource.quartzDataSource.maxConnections=10
B、任务加载及启动
@Autowired
private Scheduler scheduler;
@Autowired
private Qrtz***JobsMapper qrtz***JobsMapper;
@PostConstruct
@SuppressWarnings("unchecked")
public void execute() throws Exception{
List<Qrtz***Jobs> allJobs = qrtz***JobsMapper.queryQrtz***JobsByAll();
for(Qrtz***Jobs scheduleJob : allJobs){
TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobname(), scheduleJob.getJobgroup());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (null == trigger) {
if(scheduleJob.getStatus() == 0){
log.info("任务{}.{}状态为禁止,不启动!", scheduleJob.getJobgroup(), scheduleJob.getJobname());
continue;
}
log.info("任务{}.{}启动。", scheduleJob.getJobgroup(), scheduleJob.getJobname());
JobDetail jobDetail=null;
//创建JobDetail(数据库中job_name存的任务全路径,这里就可以动态的把任务注入到JobDetail中)
jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(scheduleJob.getJobname())).withIdentity(scheduleJob.getJobname(), scheduleJob.getJobgroup()).build();
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron());
//按新的cronExpression表达式构建一个新的trigger
trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobname(), scheduleJob.getJobgroup()).withSchedule(scheduleBuilder).build();
//把trigger和jobDetail注入到调度器
scheduler.scheduleJob(jobDetail, trigger);
}else{
if(scheduleJob.getStatus() == 0){
// 当前任务状态为禁止
log.info("任务{}.{}状态为禁止,移除任务。", scheduleJob.getJobgroup(), scheduleJob.getJobname());
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobname(), scheduleJob.getJobgroup());
scheduler.deleteJob(jobKey);
continue;
}else{
String searchCron = scheduleJob.getCron(); //获取数据库的
String currentCron = trigger.getCronExpression();
if(!searchCron.equals(currentCron)){
log.info("任务{}.{}变化,启动。", scheduleJob.getJobgroup(), scheduleJob.getJobname());
//说明该任务有变化,需要更新quartz中的对应的记录
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron);
//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
}else{
log.info("任务{}.{}无变化,正常启动。", scheduleJob.getJobgroup(), scheduleJob.getJobname());
}
}
}
}
}
C、编写任务
@Component
@DisallowConcurrentExecution
public class TestJob implements Job {
private Logger log = LoggerFactory.getLogger(TestJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
CronTrigger trigger = (CronTrigger) context.getTrigger();
String corn = trigger.getCronExpression();
String jobName = trigger.getKey().getName();
String jobGroup = trigger.getKey().getGroup();
log.info("任务表达式:{},任务名称:{},任务组:{}", corn, jobName, jobGroup);
}
}