chaosblade-io / chaosblade-box

chaos-platform
216 stars 98 forks source link

quartz 定时任务缺陷”Unable to store Job : ‘XXXXXXXXX‘, because one already“ #123

Open BobFintech opened 2 years ago

BobFintech commented 2 years ago

缺陷一

org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'DEFAULT.com.alibaba.chaosblade.box.dao.scheduler.job.ExperimentTaskAutoRecoverySchedulerJob', because one already exists with this identification. at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1113) ~[quartz-2.3.0.jar:na] at org.quartz.impl.jdbcjobstore.JobStoreSupport$2.executeVoid(JobStoreSupport.java:1067) ~[quartz-2.3.0.jar:na] at org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3765) ~[quartz-2.3.0.jar:na] at org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3763) ~[quartz-2.3.0.jar:na] at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:245) ~[quartz-2.3.0.jar:na] at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJobAndTrigger(JobStoreSupport.java:1063) ~[quartz-2.3.0.jar:na] at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:855) ~[quartz-2.3.0.jar:na] at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:249) ~[quartz-2.3.0.jar:na] at com.alibaba.chaosblade.box.dao.scheduler.quartz.QuartzSchedulerService.rescheduleCronJob(QuartzSchedulerService.java:89) ~[classes/:na] at com.alibaba.chaosblade.box.dao.scheduler.BaseSchedulerService.addSchedulerJob(BaseSchedulerService.java:41) ~[classes/:na] at com.alibaba.chaosblade.box.dao.scheduler.job.ExperimentTaskAutoRecoverySchedulerJob.afterPropertiesSet(ExperimentTaskAutoRecoverySchedulerJob.java:70) [classes/:na] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) [spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) [spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) [spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) [spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) [spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) [spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) [spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) [spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) [spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) [spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) [spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE] at com.alibaba.chaosblade.box.Application.main(Application.java:12) [classes/:na]

问题原因:第一次运行,执行QuartzSchedulerService->internalAddSchedulerJob(),此时jobname为”schedulerJob.getJobId()“ 第二次运行,执行QuartzSchedulerService->rescheduleCronJob(),此时根据schedulerJobDO.getJobId()判断是否存在进行删除job和定制trigger,然后插入以schedulerJobDO.getName() 第三次运行,执行QuartzSchedulerService->rescheduleCronJob(),此时根据schedulerJobDO.getJobId()判断是否存在进行删除job和定制trigger,然后插入以schedulerJobDO.getName(),此时以name创建job会出现该错误 建议修复:QuartzSchedulerService中85行和87行中的 schedulerJobDO.getName()->schedulerJobDO.getJobId()

缺陷二:

ERROR 26475 --- [ main] c.a.c.b.d.scheduler.SchedulerJobService : init scheduler job failed,beanClass:com.alibaba.chaosblade.box.scheduler.AgentPingScheduleJob,name:AgentPingScheduleJob,exist job 重复执行QuartzSchedulerService中的initPersistenceSchedulerJob()方法与初始化重复 建议修复方案一:注释掉该方法 建议修复方案二:QuartzSchedulerService的addSchedulerJob()方法中添加判断

private void addSchedulerJob(Class beanClass, String name, ApplicationContext applicationContext,
        String cronExpression, Date startTime, long startDelay, JobDataMap jobDataMap) {
        if (Strings.isNullOrEmpty(cronExpression)) { return; }
        TriggerKey triggerKey = new TriggerKey(getTriggerName(name));
        try {
           //新增判断
            if(scheduler.checkExists(triggerKey)){
                Trigger trigger = scheduler.getTrigger(triggerKey);
                scheduler.deleteJob(trigger.getJobKey());
                scheduler.pauseTrigger(triggerKey);
            }
            JobDetail jobDetail = initJobDetail(beanClass, name, applicationContext, jobDataMap);
            Trigger trigger = initTrigger(jobDetail, name, cronExpression, startTime, startDelay);
            scheduler.scheduleJob(jobDetail, trigger);
            log.info("add scheduler job success,job class:{},name:{}", beanClass.getName(),
                name);
        } catch (Exception ex) {
            if (ex instanceof ObjectAlreadyExistsException) {
                log.error(
                    "init scheduler job failed,beanClass:" + beanClass.getName() + ",name:" + name + ",exist job");
            } else {
                log.error(
                    "init scheduler job failed,beanClass:" + beanClass.getName() + ",name:" + name, ex);
            }
        }
    }
BobFintech commented 2 years ago
@Override
    protected void rescheduleCronJob(SchedulerJobDO schedulerJobDO) throws Exception {
        TriggerKey triggerKey = new TriggerKey(getTriggerName(schedulerJobDO.getJobId()));
        boolean exist = scheduler.checkExists(triggerKey);
        if (exist) {
            Trigger trigger = scheduler.getTrigger(triggerKey);
            scheduler.deleteJob(trigger.getJobKey());
            scheduler.pauseTrigger(triggerKey);
            JobDataMap jobDataMap = new JobDataMap();
            fillJobDataMap(jobDataMap, schedulerJobDO);
            //修改为:chedulerJobDO.getJobId(),
            JobDetail jobDetail = initJobDetail(loadClassFromSpring(schedulerJobDO), schedulerJobDO.getJobId(),
                applicationContext, jobDataMap);
            //修改为:chedulerJobDO.getJobId(),
            trigger = initTrigger(jobDetail, schedulerJobDO.getJobId(), schedulerJobDO.getCronExpression(),
                schedulerJobDO.getStartTime(), 2000);
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            internalAddSchedulerJob(schedulerJobDO);
        }
    }
MandssS commented 2 years ago

你好,非常感谢你的提议,你这边可以直接整体成pr,直接提交过来