Search code examples
javaspring-bootquartz-schedulerjava-threadsquartz

Quartz pauses for a period of time and then resumes, it will quickly execute the tasks during the pause, and then execute according to corn


My English is not very good, I use Google Translate, which may cause trouble for everyone's reading, sorry

I want to use quartz to implement a scheduled push task, but there are some problems;

I will show my problems and attempts now, hoping to get help from everyone, thank you!

I am not using a database for persistence , here is my test code for adding task,some location pointers that need to push data are recorded in the JobDataMap

@Override
    public int addJob(JobInfo jobInfo) {
        int result = 0;
        int isJobExist = this.isJobExist(JobKey.jobKey(jobInfo.getJobName(), jobInfo.getJobGroup()));
        if (isJobExist == 1) {
            result = -1;
            log.info("Task already exists");
        } else {
            try {
                JobDetail jobDetail = null;
                if (isJobExist == 0) {
                    jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobInfo.getJobName(), jobInfo.getJobGroup()));
                } else if (isJobExist == -1) {
                    jobDetail = JobBuilder.newJob(
                            (Class<? extends QuartzJobBean>) Class.forName(jobInfo.getJobClassname()))
                            .withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup())
                            .withDescription(jobInfo.getDescription())
                            .storeDurably(jobInfo.isDurability()).build();
                    if(jobInfo.getNpcParam() != null){
                        jobDetail.getJobDataMap().put(NpcParam.NPC_JOB_PARAM, jobInfo.getNpcParam());
                    }
                }
                // If the cron expression of jobInfo is empty, a regular task will be created, 
                // otherwise a periodic task will be created
                if (!StringUtils.isEmpty(jobInfo.getCronExpression())) {
                    CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                            .withIdentity(jobInfo.getTriggerName(), jobInfo.getTriggerGroup())
                            .withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCronExpression()))
                            .build();
                    scheduler.scheduleJob(jobDetail, cronTrigger);
                } else {
                    Trigger trigger = TriggerBuilder.newTrigger()
                            .withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup())
                            .startAt(sdf.parse(jobInfo.getNextFireTime()))
                            .withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(0))
                            .build();
                    scheduler.scheduleJob(jobDetail, trigger);
                }
            } catch (ClassNotFoundException e) {
                result = 1;
                log.error("The Class corresponding to the task does not exist!");
            } catch (SchedulerException e) {
                result = 2;
                e.printStackTrace();
                log.error("task scheduling failed");
            } catch (ParseException e) {
                result = 3;
                log.error("time conversion error");
            }
        }
        return result;
    }

pause method

scheduler.pauseJob(JobKey.jobKey(jobName, jobGroup));

resume method

scheduler.resumeJob(JobKey.jobKey(jobName, jobGroup));

But this will cause the problem in the title, the data will be pushed to the front end soon

I try to delete the job while suspending the job, get the location pointer of the pushed data before deleting, and create a new job when continuing.

While this solves the above problem, I think there may be hidden dangers:

Quartz deletes the job and waits for the execution of the current job to complete. The deleted job and the scheduled job are two different threads, but I cannot ensure that the obtained location pointer is the latest, which will cause a set of already pushed data to be pushed next time.


Solution

  • I think I found a solution. I added the quartz.properties file in the resources directory of the project and set the following content

    org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
    org.quartz.jobStore.misfireThreshold=100
    # others
    

    In this way, I will not redo the work when I resume after a period of suspension, and the artificial operation can hardly be faster than 100ms

    Quartz scheduler misfire instructions explained

    RAMJobStore

    I didn't add it to quartz.properties at first, but I set properties in SchedulerFactoryBean, but it didn't seem to take effect

    For thread safety issues, I choose to lock for different jobkeys