#Quartz#
项目启动要初始化程序配置的各个job, 采用的是quartz,但是发现有时候启动时报错:org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'MONITOR_RUNNER_GROUP.indexWarningRunner', because one already exists with thi s identification, job存储失败。
查看quartz数据库: cron_triggers,里面没有 MONITOR_RUNNER_GROUP.indexWarningRunner, job_details库里面有,triggers库里面没有。
存不进去是因为里面已经有了,删除job_details里面的,启动正常,但是后续还会出现这种情况。代码逻辑有问题,不是什么quartz bug。
初始化quartz任务代码:
public void init() throws Exception { Map<String, BaseRunner> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType(BaseRunner.class); for (String name : beansOfType.keySet()) { BaseRunner baseRunner = beansOfType.get(name); JobKey jobKey = new JobKey(name, RunnerConstants.RUNNER_GROUP); TriggerKey triggerKey = new TriggerKey(name, RunnerConstants.RUNNER_GROUP);
RunnerCron annotation = baseRunner.getClass().getAnnotation(RunnerCron.class); if (annotation == null) { LOG.warn("monitor schedule: {} create failed, cron is missing", jobKey.toString()); this.tryDeleteJob(jobKey); continue; } String cron = this.getProperties(annotation.value()); if (StringUtils.isNotBlank(cron)) { JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean(); jobDetailFactoryBean.setGroup(RunnerConstants.RUNNER_GROUP); jobDetailFactoryBean.setName(name); jobDetailFactoryBean.setDurability(true); jobDetailFactoryBean.setJobClass(RunnerJob.class); jobDetailFactoryBean.getJobDataMap().put(RunnerConstants.RUNNER_NAME, name); jobDetailFactoryBean.afterPropertiesSet(); JobDetail jobDetail = jobDetailFactoryBean.getObject();
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); if (Objects.isNull(trigger) ) { CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(CronScheduleBuilder.cronSchedule(cron)).startNow().build(); scheduler.scheduleJob(jobDetail, cronTrigger); LOG.info("monitor schedule: {} created", triggerKey.toString()); } else { CronTrigger cronTrigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(CronScheduleBuilder.cronSchedule(cron)).startNow().build(); scheduler.rescheduleJob(triggerKey, cronTrigger); LOG.info("monitor schedule: {} refreshed", triggerKey.toString()); } } else { LOG.warn("monitor schedule: {} create failed, cron is invalid", jobKey.toString()); this.tryDeleteJob(jobKey); } } }
private void tryDeleteJob(JobKey jobKey) throws SchedulerException { TriggerKey triggerKey = TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()); scheduler.pauseTrigger(triggerKey); //这句话是先删除相关的trigger触发器,如果trigger关联的job没有其他触发器并且不持续,也被删除(duarble为true,只删除trigger,job还在保留) //否则下次启动就有可能存储job的时候报错 //scheduler.unscheduleJob(triggerKey);
//会删除joddetail,以及其关联的triggers scheduler.deleteJob(jobKey); }
可以看出来,如果trigger 是空,但是job 数据库还是有的。然后scheduleJob方法,存储job报错了。
最终做法是:
虽然,一个job可以绑定多个trigger,一个trigger只能有一个job, 但是这里就做成了一对一的关系,避免混乱:判断trigger和jobDetail 有没有,都有,就更新trigger,否则deleteJob ,然后新建。或者,启动的时候一并删除,然后全部新建