[Timing Scheduling]- 01 Do you really understand the basis of quartz?

Past recommendation

[RabbitMQ analysis] 01 SimpleMessageListenerContainer principle analysis

[sharding-sphere] - 01 SQL Routing

[Nacos source code analysis] - 02 Obtaining the configuration process

[Java Concurrent Programming] - 03 MESI, memory barrier

[Spring source code]- 11 Programmatic transactions of Spring AOP

overview

QuartzAn open source task scheduling framework with rich functions and a long history, which is completely based on Java. It is very well-known in the field of Java scheduling. Its easy-to-use, stable and reliable features make it regarded as the basic dependency of the scheduling framework by many third-party applications. If it has built-in spring bootintegration quartz, elastic-jobthe scheduling framework will quartzbe packaged as its underlying basic implementation. xxl-jobHistorical versions were also integrated quartzas its trigger The implementation mechanism is basic, but the implementation of the time wheel has been quartzremoved in the latest version.

core trident

When in use quartz api, the most core three-piece suit is as follows:

Scheduler

SchedulerFactoryAnd Schedulerit is easy to identify from the name. The factory design pattern is adopted here, which Scheduleris quartzthe most important component exposed for development. From the developer's perspective, it is the quartzfacade of the factory. quartzVarious operations are Schedulerconnected in series, similar quartzto The role of butler and spokesperson.

This design pattern is very common in open source frameworks, such as mybatisZhonghe , which simplifies the difficulty SqlSessionFactoryfor SqlSessiondevelopers to get started by providing developers with big housekeeper components and connecting all core functions in series through one component.

Generally, an application only corresponds to one Schedulerinstance, and different Schedulerinstances are isolated through schedulerNameisolation. All quartzdatabase table designs have this column field, so that the Scheduler will only operate on the corresponding data sched_namein the database table when processing tasks . Clustering is to use multiple instances to configure the same name, so that multiple machines can process the same task at the same time to achieve the cluster effect.schedulerNamequartzSchedulerschedulerNameschedulerName

schedulerNameCan be org.quartz.scheduler.instanceNameconfigured via , the default name is QuartzScheduler.

SchedulerThe operation is mainly related JobDetailto Triggertwo components, JobDetailwhich encapsulate task configuration information, and Triggertriggers encapsulate task trigger information. They are 1:Nrelationships, that is, one JobDetailcan be associated with multiple Triggertriggers, but one Triggertrigger can only be bound to one job. .

JobDetail

JobDetailThe component encapsulates quartzthe scheduling task definition information, the following is JobDetailthe general usage of the component as follows:

// JobDataMap实现Map接口,任务调度时存储到JobExecuteContext中,可以传递给Job实例
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("name", "zhangsan");
jobDataMap.put("time", System.currentTimeMillis());

JobDetail jobDetail = JobBuilder
      // 绑定任务类
            .newJob(QuartzCronJob.class)
            .storeDurably()
      // job对应ID
            .withIdentity("job2", "DEFAULT") 
            .usingJobData(jobDataMap)
            .build();

JobKey jobKey = jobDetail.getKey();
if (scheduler.checkExists(jobKey)) {
 log.warn("调度任务已存在,删除后重新添加:{}", jobKey);
 scheduler.interrupt(jobKey);//停止JOB
    /**
      * deleteJob操作在删除Job之前,会执行unscheduleJob()取消job和trigger关联
    */
    scheduler.deleteJob(jobKey);
}
// 将JobDetail任务定义信息插入quartz表
scheduler.addJob(jobDetail, true);

JobDetailThe operation is relatively simple, and there are two main points to note: 1. newJob(Class <? extends Job> jobClass)The operation is bound to the task class, which is the class that encapsulates the user's business logic; 2. withIdentity(String name, String group)Set an identity ID for the task, which can be used for subsequent management, for convenience and flexibility The management quartzabstracts groupthe concept, so that batch operations can be performed on a group of jobs in batches, and the identity ID is used for JobKeyencapsulation.

Use Schedulerthe class addJob(JobDetail jobDetail, boolean replace)method to add the created Jobdefinition information to it quartz. Generally, the database persistence mode is used, that is, Jobthe definition information will be inserted into qrtz_job_detailsthe table here (see the figure below).

Let's look at the following key fields:

sched_name:上面说过,用来关联对应的Scheduler实例

is_durable:是否持久化

is_nonconcurrent:是否允许同一个作业可以同时多个实例执行,比如一个任务间隔1秒,但其执行时间为2秒,通过该属性控制是否允许同一个作业有多个任务同时允许,参见@DisallowConcurrentExecution

is_update_data: 任务已经执行中,是否允许更新JobDataMap持久化信息,参见@PersistJobDataAfterExecution

requests_recovery: 故障恢复使用,具体参见后续源码分析

job_data:JobDataMap序列化后存储到字段中

Trigger

The task definition is complete, but the task is triggered according to the periodic rules, it depends on Triggerthe face of the trigger

TriggerThe general usage of components 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();//时间


TriggerKey triggerKey = trigger.getKey();
if(scheduler.checkExists(triggerKey)){
 scheduler.unscheduleJob(triggerKey);
}
//必须绑定job
scheduler.scheduleJob(trigger);

Similar to and JobDetail, there are two main points to note: 1. Similarly withIdentity(String name, String group), set an identity ID for the trigger in the same way, corresponding to ; TriggerKey2. startAt(), corresponding to endAt()the start and stop time; 3. withSchedule(CronScheduleBuilder.cronSchedule("*/10 * * * * ?")); Task.forJob(JobKey keyOfJobToFire)TriggerJob

Finally, add the created definition information to it through Schedulerthe class method . Generally, the database persistence mode is used, that is, the definition information will be inserted into the trigger-related table here. In the example using the trigger, it will be inserted into the table (see below picture).scheduleJob(Trigger trigger)TriggerquartzTriggercronqrtz_cron_triggers

Let's take a look at how the task is triggered. SchedulerAfter the class scheduleJob(Trigger trigger)persists the trigger, you will find that qrtz_cron_triggersthere is no start and end time and Jobbinding content, so let's look at a very important table: qrtz_triggers. The method will insert a record into the table at the same time after scheduleJob()persisting the information (see the figure below):Triggerqrtz_triggers

qrtz_job_detailsAnd qrtz_cron_triggerscan be regarded as a static table, that qrtz_triggersis, the running dynamic table, which stores the data during the running of the task and changes dynamically with the running records. It is quartzthe most important table for scheduling task running. Let's take a look at some key points in this table field:

start_time、end_time: trigger定义时设置的起止时间
next_fire_time: 下次触发时间戳
prev_fire_time: 上次触发时间戳

trigger_state: trigger状态,最常见状态WAITING、ACQUIRED和EXECUTING,分别对应等待(下次触发时间还早) -> 加载到内存中等待(下次触发时间快到了) --> 执行(下次触发时间到了,需要触发任务),具体参见后续源码分析


misfire_instr: trigger触发时间过期处理策略,比如本来是10:23:50时间点进行触发,但是由于某些原因在10:23:53秒才检索出来,这是该触发时间点已经过期,misfire_instr就是控制采用什么策略处理该过期任务,是直接丢弃重新计算下次触发时间点、还是一定时间范围内过期的理解执行等等,具体参见后续源码分析

job_data: 和JobDetail一样,Trigger也可绑定一个JobDataMap,用于向Job实例传递参数,该字段就是存储Trigger关联的JobDataMap序列化内容

quartzBasically, it is to realize the task triggering around these key fields. We can roughly come up with a rough process of the task scheduling trigger mechanism qrtz_triggersby guessing :quartz

1. Through the configured triggertrigger, calculate the next trigger time, update to next_fire_timethe field, and update trigger_statethe status as WAITING;

2. quartzThe thread scans the table, queries the records that will be triggered in a short period of time in the future (comparison next_fire_timeand current time) into the memory queuing queue, and then trigger_stateupdates to ACQUIRED;

3. Then block until the trigger task in the memory queuing queue reaches the time point, before triggering the task, recalculate the next trigger time point, update to, and update to, and then next_fire_timeexecute trigger_statethe WAITINGcurrent task;

4. Since next_fire_timethe sum trigger_statevalue is updated, start step 1 again, and the triggering cycle continues like this.

Summarize

This section simply analyzes the core operating mechanism from the perspective of a user quartz. Since it simply analyzes the source code from the outer layer without in-depth analysis, it simply quartzmakes a simple guess about the approximate operating mechanism based on the information in the database table, and some important attributes have not been expanded. Follow these questions in the next section to find the real answer through source code analysis, and deepen quartzthe understanding of the operating mechanism step by step.

Long press the QR code below to identify attention           

Guess you like

Origin blog.csdn.net/god_86/article/details/115222814