ObjectAlreadyExistsException:Unable to store Job : '*', because one already exists with thi s ident

    xiaoxiao2022-07-02  126

    #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 ,然后新建。或者,启动的时候一并删除,然后全部新建

     

     

     

     

    最新回复(0)