An overview of the

Quartz is a rich, time-honored, open source task scheduling framework based entirely on Java. It is easy to use, stable and reliable, so that many third-party applications rely on it as the basic scheduling framework. For example, Spring Boot has built-in Integration of Quartz, and Elastic job scheduling framework encapsulates Quartz as its underlying basic implementation. The previous version of xxl-job also integrated Quartz as the basis of its trigger implementation, but the time wheel implementation has removed Quartz in the latest version.

Core trident

When using the Quartz API, there are three core pieces:

Scheduler

SchedulerFactory and Scheduler are easily recognized by their names. The factory design pattern is used here. Scheduler is one of the most important components of Quartz exposed for development, and it is the face of Quartz from a developer’s perspective. The various operations on Quartz are concatenated through Scheduler, similar to the role of quartz’s butler and spokesperson.

This design pattern is very common in open source frameworks, such as SqlSessionFactory and SqlSession in Mybatis. It simplifies the difficulty of getting started with the framework by providing developers with a big manager component that connects all the core functions in a single component.

An application typically has only one Scheduler instance, which is isolated by schedulerName. All Quartz database table designs have sched_name. In this case, the Scheduler only operates on the data under the corresponding schedulerName in the database table. Quartz cluster uses multiple Scheduler instances to configure the same schedulerName name, so that multiple machines can simultaneously process tasks under the same schedulerName to achieve cluster effect.

SchedulerName by org. Quartz. The scheduler. InstanceName is configured, the default name for the QuartzScheduler.

Scheduler mainly operates JobDetail and Trigger components. JobDetail encapsulates task configuration information, while Trigger Trigger encapsulates task Trigger information. They are 1: N relation, that is, a JobDetail can be associated with multiple Trigger triggers, but one Trigger Trigger can be bound to only one Job.

JobDetail

The JobDetail component encapsulates the Quartz scheduling task definition information. The general usage of the JobDetail component is as follows:

// JobDataMap implements the Map interface, which is stored in JobExecuteContext during task scheduling and can be passed to the Job instance
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("name"."zhangsan");
jobDataMap.put("time", System.currentTimeMillis());

JobDetail jobDetail = JobBuilder
    		// Bind the task class
            .newJob(QuartzCronJob.class)
            .storeDurably()
    		// Job corresponds to the ID
            .withIdentity("job2"."DEFAULT") 
            .usingJobData(jobDataMap)
            .build();

JobKey jobKey = jobDetail.getKey();
if (scheduler.checkExists(jobKey)) {
	log.warn("Scheduling task already exists, delete and add again :{}", jobKey);
	scheduler.interrupt(jobKey);/ / stop the JOB
    /** * deleteJob Before deleting a Job, unscheduleJob() is executed to remove the association between the Job and trigger */
    scheduler.deleteJob(jobKey);
}
// Insert the JobDetail task definition into the Quartz table
scheduler.addJob(jobDetail, true);
Copy the code

1. NewJob (Class
jobClass) the extends Job> jobClass operation binds a task class that encapsulates the user’s business logic. 2, withIdentity(String name, String group) Set an identity ID for the task, the subsequent management can be based on the identity ID, for convenient and flexible management quartz abstract the concept of group, so that a group of jobs can be batch operation, The identity ID is encapsulated with JobKey.

The Scheduler class addJob(JobDetail JobDetail, Boolean replace) method is used to add the Job definition information to Quartz, usually in database persistence mode. This is where the Job definition information is inserted into the qrtz_job_details table (see figure below).

Let’s look at the following key fields:

Sched_name: Is_durable: Persistent is_NonConcurrent: Whether the same job can be executed by multiple instances at the same time. For example, the execution time of a task is 2 seconds after the interval of 1 second. Through this property controls whether to allow the same homework with multiple tasks at the same time allow, see @ DisallowConcurrentExecution is_update_data: Task has been executed, whether to allow the update JobDataMap persistent information, see the @ PersistJobDataAfterExecution requests_recovery: For details, see subsequent source code analysis. Job_data :JobDataMap is serialized and stored in fieldsCopy the code

Trigger

The task definition is complete, but the task is executed according to the periodic rules, depending on the face of the Trigger Trigger!

The general usage of the Trigger component is as follows:

JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("name"."lisi");
jobDataMap.put("address"."China");

Trigger trigger = TriggerBuilder
            .newTrigger()
            .withIdentity("trigger1"."DEFAULT")
            .usingJobData(jobDataMap)
            .startAt(new Date())
            .endAt(new Date(System.currentTimeMillis()+38 * 60 * 1000))
            .withSchedule(CronScheduleBuilder.cronSchedule("*/10 * * * *?"))
            .forJob(new JobKey("job1"."DEFAULT"))
            .build();/ / time


TriggerKey triggerKey = trigger.getKey();
if(scheduler.checkExists(triggerKey)){
	scheduler.unscheduleJob(triggerKey);
}
// Job must be bound
scheduler.scheduleJob(trigger);
Copy the code

WithIdentity (String name, String group) TriggerKey = withIdentity(String name, String group); 2. StartAt () and endAt() correspond to the start and end time; 3, withSchedule (CronScheduleBuilder cronSchedule (” * * * * * / 10?” )); 4. ForJob (JobKey keyOfJobToFire) Associates the Trigger with the Job to know which task is triggered.

Finally, Scheduler class scheduleJob(Trigger Trigger) method is used to add the created Trigger definition information to Quartz. Generally, the database persistence mode is adopted, that is, the Trigger definition information is inserted into the Trigger related table. The CRon triggers used in the example are inserted into the qrTZ_cron_Triggers table (see figure below).

Let’s take a look at how the quest is triggered. After the Scheduler class scheduleJob(Trigger Trigger) persists the Trigger, you will notice that qrtz_cron_triggers has no start and end times and no binding to the Job, so let’s look at a very important table :qrtz_triggers. The scheduleJob() method inserts a record into the qrTZ_triggers table after persisting Trigger information (see figure below) :

Qrtz_job_details and qrtz_cron_triggers can be regarded as static tables. Qrtz_triggers is a running dynamic table, which saves the data during the running of the task and changes dynamically with the running records. It is the most important table for the running of quartz scheduling tasks. Let’s look at some of the key fields in this table:

Start_time, end_time: indicates the start and end time set when trigger is defined. Next_fire_time: indicates the next trigger time prev_FIRE_time: indicates the last trigger time trigger_state: indicates the last trigger time. Trigger state, the most common states are WAITING, required and EXECUTING, corresponding to wait (next triggering time is early) -> Loading to memory wait (next triggering time is near) -> EXECUTING (task needs to be triggered when next triggering time is up), See misfire_instr for further source code analysis: For example, misfire_instr controls which policy is used to process the expired task. Misfire_instr controls which policy is used to process the expired task. Whether to discard directly to recalculate the next trigger point, or expired understanding execution within a certain time range, etc. For details, see the subsequent source code analysis job_data: Like JobDetail, Trigger can also be bound to a JobDataMap, which is used to pass parameters to the Job instance. This field is the JobDataMap serialized content that stores Trigger's associationCopy the code

Quartz is basically implementing task triggering around the key fields triggered by QrTZ_triggers. We can roughly figure out the process of quartz task triggering mechanism:

Trigger_state = WAITING; trigger_state = WAITING;

The Quartz thread scans the table and queries the records that will be triggered in a short time in the future (next_fire_time vs. current time) into the memory queue, and then updates the trigger_state to “ACQUIRED”.

3, then block until the memory queue triggers the task to the point in time, before triggering the task, recalculate the next trigger time, update to next_fire_time, update trigger_state to WAITING, and execute the current task;

4. Restart Step 1 because the next_FIRE_time and trigger_state values are updated, and the cycle continues.

conclusion

This section from the perspective of a user quartz core operation mechanism, simple analysis without further analysis by simply from outer source code, a simple database table information on the running mechanism of quartz is roughly do a simple hypothesis, some important properties also did not expand, with these questions in the next section, through the analysis of the source code to find the real answer, Step by step to deepen the understanding of quartz operation mechanism.