Quartz task scheduling
1, The concept of Quartz
1. Basic introduction
Quartz is another open source project of OpenSymphony in the field of Job scheduling. It can be combined with J2EE and J2SE applications or used alone.
Quartz is an open source "task scheduling library" with rich features, which can be integrated into any Java application, from independent application to e-commerce system. Quartz can create simple and complex schedules to perform tens, hundreds, even tens of thousands of tasks. Task job is defined as a standard Java component, which can perform any function you want. Quartz scheduling framework includes many enterprise level features, such as JTA transaction and cluster support.
In short, Quartz is a Java based task scheduling framework for any task you want to perform.
Official website: http://www.quartz-scheduler.org/
Official document: http://www.quartz-scheduler.org/documentation/
Original address: https://github.com/quartz-scheduler/quartz
2. Quartz running environment
- Quartz can run embedded in another stand-alone application
- Quartz can be instantiated in an application server (or Servlet container) and participate in transactions
- Quartz can be run as a stand-alone program (in its own Java virtual machine) and can be used through RMI
- Quartz can be instantiated as an independent project cluster (load balancing and fail over functions) for job execution
3. The core concept of quartz
-
Task Job
Job is the task class you want to implement. Every job must implement the org.quartz.job interface, and only need to implement the execute() method defined by the interface.
-
Trigger trigger
Trigger for you to perform tasks. For example, if you want to send a statistical email at 3 o'clock every day, trigger will set 3 o'clock to perform the task.
There are two kinds of Trigger: SimplerTrigger and CronTrigger. See 7.9 and 7.10 for details -
Scheduler
The Scheduler is the Scheduler of the task, which integrates the task Job and Trigger trigger, and is responsible for executing the Job based on the time set by Trigger.
4. Architecture of quartz
2, The use of Quart
1. Introducing jar package of Quartz
Create a springboot (version: 2.2.4.RELEASE) application and introduce the dependency directly
<dependencies> <!-- Quartz Core package --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> </dependency> <!-- Quartz tool kit --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> </dependency> </dependencies>
2. Introduction case
(1) Create HelloJob task class
// Define task class public class HelloJob implements Job { @Override public void execute(JobExecutionContext arg0) throws JobExecutionException { // Output current time ystem.out.println(new Date()); } }
(2) Creating a task scheduling class HelloSchedulerDemo
public class HelloSchedulerDemo { public static void main(String[] args) throws Exception { // 1. Scheduler to obtain the instance of the schedule from the factory (default: instantiate new StdSchedulerFactory();) Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 2. The Job detail defines a task scheduling instance, which is bound to HelloJob. The task class needs to implement the Job interface JobDetail jobDetail = JobBuilder.newJob() // Load the task class, complete the binding with HelloJob, and require HelloJob to implement the Job interface .withIdentity("job1", "group1") // Parameter 1: the name of the task (unique instance); Parameter 2: the name of the task group .build(); // 3. Trigger defines a trigger, executes immediately, and then repeats every 5 seconds Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") // Parameter 1: name of trigger (unique instance); Parameter 2: the name of the trigger group .startNow() // Trigger now .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()) // Repeat every 2 seconds .build(); // 4. Let the scheduler associate tasks and triggers to ensure that tasks are executed according to the adjustment defined by triggers scheduler.scheduleJob(jobDetail, trigger); // 5. Start up scheduler.start(); // close //scheduler.shutdown(); } }
3. Job and JobDetail
- Job: the interface of work task scheduling, which the task needs to implement. This interface defines the execute method, which is similar to the run method of the TimeTask class provided by JDK. Write the business logic of task execution in it.
- Life cycle of Job instance in Quartz: every time the scheduler executes a Job, it will create a new Job instance before calling the execute method. When the call is completed, the associated Job object instance will be released, and the released instance will be recycled by the garbage collection mechanism.
- JobDetail: JobDetail provides many setting properties for Job instances, as well as the member variable properties of JobDataMap, which are used to store the status information of specific Job instances. The scheduler needs to add Job instances with the help of the JobDetail object.
- Important properties of JobDetail: name, group, jobClass, JobDataMap
JobDetail job = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") // Define the unique identity of the instance and specify a group .build(); System.out.println("name:" +job.getKey().getName()); System.out.println("group:" +job.getKey().getGroup()); System.out.println("jobClass:" +job.getJobClass().getName());
4,JobExecutionContext
- When the Scheduler calls a Job, it will pass the JobExecutionContext to the execute() method of the Job;
- Job can access the environment of Quartz runtime and the detailed data of job itself through JobExecutionContext object.
public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { Trigger trigger = jobExecutionContext.getTrigger(); //Get Trigger JobDetail jobDetail = jobExecutionContext.getJobDetail(); //Get JobDetail Scheduler scheduler = jobExecutionContext.getScheduler(); //Get Scheduler trigger.getKey().getName(); //Get Trigger name trigger.getKey().getGroup(); //Get Trigger group name (DEFAULT) jobExecutionContext.getScheduledFireTime(); //The scheduled time when the trigger is triggered. jobExecutionContext.getFireTime(); //Actual trigger time. For example, the scheduled time may be 10:00:00, but if the scheduler is too busy, the actual trigger time may be 10:00:03. jobExecutionContext.getPreviousFireTime(); //Last trigger time jobExecutionContext.getNextFireTime(); //Next trigger time System.out.println(new Date()); } }
5. Introduction to JobDataMap
(1) Using Map to get
- When scheduling tasks, the JobDataMap is stored in the JobExecutionContext, which is very convenient to obtain.
- JobDataMap can be used to load any serializable data object, and these parameter objects will be passed to the Job instance object when it is executed.
- JobDataMap implements the Map interface of JDK, and adds a very convenient method to access the basic data types.
When defining a Trigger or Job Detail, pass in the JobDataMap, and then you can get the parameters in the JobDataMap
public class HelloScheduler { public static void main(String[] args) throws SchedulerException { //1. Scheduler Scheduler defaultScheduler = StdSchedulerFactory.getDefaultScheduler(); JobDataMap jobDataMap2 = new JobDataMap(); jobDataMap2.put("message", "JobDetailMessage"); //2. Task instance (JobDetail) JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "jobGroup1") .usingJobData(jobDataMap2) .build(); // Define JobDataMap JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("message", "TriggerMessage"); //3. Trigger Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "triggerGroup1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()) .endAt(new Date(new Date().getTime() + 3000L)) .usingJobData(jobDataMap) // Put JobDataMap into Trigger .build(); defaultScheduler.scheduleJob(jobDetail, trigger); defaultScheduler.start(); } }
HelloJob.java
public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println(jobExecutionContext.getTrigger().getJobDataMap().get("message")); //TriggerMessage System.out.println(jobExecutionContext.getJobDetail().getJobDataMap().get("message")); //JobDetailMessage System.out.println(jobExecutionContext.getMergedJobDataMap().get("message")); //TriggerMessage System.out.println(new Date()); } }
(2) Using the Setter method to get
The key value of the Job datamap corresponding to the setter method is added to the Job implementation class. The default JobFactory implementation class of the Quartz framework will automatically call these setter methods when initializing the Job instance object.
The helloschedule class is the same as above.
HelloJob.java:
@Data public class HelloJob implements Job { private String message; @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println(message); //TriggerMessage System.out.println(new Date()); } }
Note: if a key with the same name is encountered, the value of JobDataMap in Trigger will override the key with the same name in JobDetail
6. Stateful jobs and stateless jobs (@ PersistJobDataAfterExecution)
A stateful Job can be understood as holding some state information during multiple Job calls, which is stored in the JobDataMap, while the default stateless Job creates a new JobDataMap every time it is called.
(1) Modify HelloSchedulerDemo.java. Add. usingJobData("count", 0) to JobDetail to indicate the counter.
JobDetail job = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") // Define the unique identity of the instance and specify a group .usingJobData("message", "Print log") .usingJobData("count", 0) .build();
(2)HelloJob.java
@Data @PersistJobDataAfterExecution public class HelloJob implements Job { private Integer count; @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println(++count); jobExecutionContext.getJobDetail().getJobDataMap().put("count", count); } }
HelloJob class does not add @ PersistJobDataAfterExecution annotation. A new JobDataMap is created every time it is called. It doesn't add up.
HelloJob class adds @ PersistJobDataAfterExecution annotation, which can hold some state information during multiple calls, that is, count accumulation can be realized.
7,Trigger
(1) SimpleTrigger trigger
SimpleTrigger is the simplest quartztigger for setting and using.
It is designed for jobs that need to be started at a specific date / time and repeated n times at a possible interval.
Case 1: it means to execute a task within a specified period of time;
Common methods of SimpleTrigger are as follows:
method | explain |
---|---|
startNow() | When the Scheduler starts executing, the trigger executes |
startAt(new Date()) | Start execution at the specified time |
withIntervalInSeconds(2) | Execution interval, corresponding time unit in method name |
repeatForever() | Repeat it all the time |
withRepeatCount(3) | Repeat the specified number of times |
endAt(new Date()) | End time |
example:
//Start execution immediately, once every 2 seconds, repeat 3 times, and end execution after 3 seconds (when one of the repeat times or end time reaches first, execution will stop) Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "triggerGroup1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).withRepeatCount(3)) .endAt(new Date(new Date().getTime() + 3000L)) .build();
Points to pay attention to
- The properties of SimpleTrigger are: start time, end time, repeat times and repeat interval.
- The value of the repeat property can be 0, a positive integer, or the constant SimpleTrigger.REPEAT_INDEFINITELY.
- The repeated time interval attribute value must be a positive integer greater than 0 or long shaping, with milliseconds as the time unit. When the repeated time interval is 0, it means triggering execution with Trigger at the same time.
- If there is a specified end time attribute value, the end time attribute takes precedence over the number of repetitions attribute. The advantage is that when we need to create a Trigger that triggers every 10 seconds until the specified end time, instead of calculating the number of repetitions from the beginning to the end, we just need to specify the end time and use REPEAT_INDEFINITELY can be used as the attribute value of the number of repetitions.
(2) CronTrigger trigger
If you need to trigger tasks on schedule like a calendar, rather than at specific intervals like SimpleTrigger, CronTrigger is usually more useful than SimpleTrigger because it is a calendar based job scheduler.
- cron expression: CRON expression
- Online cron generation: https://www.pppet.net/ , or Baidu online cron is OK
Case study:
Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")) // calendar .build();
8, SchedulerFactory
Quartz is a modular architecture, so to make it work, several components must be well integrated. Fortunately, there are some existing assistants who can do the work.
All Scheduler instances are created by SchedulerFactory.
There are three core concepts of Quartz: scheduler, task and trigger
As we all know, the three most important elements of a Job are Scheduler, JobDetail and trigger; Trigger is like a driver for a Job. If there is no trigger to drive the Job regularly, the Job cannot run; For a Job, a Job can correspond to multiple triggers, but for a trigger, a trigger can only correspond to one Job, so a trigger can only be assigned to one Job; If you need a more responsible trigger plan, you can create multiple triggers and assign them to the same Job.
(1)StdSchedulerFactory
The default SchedulerFactory of Quartz
- Use a set of parameters (java.util.Properties) to create and initialize the Quartz scheduler
- Configuration parameters are generally stored in the quartz.properties file
- Calling the getScheduler method creates and initializes the scheduler object
Creation method:
//Static method Scheduler defaultScheduler = StdSchedulerFactory.getDefaultScheduler(); //Example method StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = stdSchedulerFactory.getScheduler();
Common methods:
scheduler.scheduleJob(jobDetail, trigger); //Binding jobDetail and trigger scheduler.start(); //Start task scheduling scheduler.pauseJob(); //Task scheduling is suspended, that is, operation is suspended scheduler.standby(); //Task scheduling is suspended, that is, operation is suspended scheduler.shutdown(); //Close task scheduling, the same as shutdown(false) scheduler.shutdown(true); //It means to wait for all jobs to be executed before closing the Scheduler scheduler.shutdown(false); // Indicates to close the Scheduler directly
(2) DirectSchedulerFactory (understanding)
DirectSchedulerFactory is a direct implementation of SchedulerFactory, through which you can directly build schedulers, threadpools, etc
DirectSchedulerFactory directSchedulerFactory = DirectSchedulerFactory.getInstance(); Scheduler scheduler = directSchedulerFactory.getScheduler();
9, Quartz.properties
Default path: org.quartz.quartz.properties in quartz-2.3.2
We can also add the quartz.properties file under the resources of the project to cover the underlying configuration file.
#=============================================================== #Configure Main Scheduler Properties scheduler properties #=============================================================== #The instance name of the scheduler org.quartz.scheduler.instanceName = QuartzScheduler #The instance ID of the scheduler can be set to AUTO in most cases org.quartz.scheduler.instanceId = AUTO #=============================================================== #Configure ThreadPool thread pool properties #=============================================================== #The number of threads to process jobs should be at least 1, but it's better not to exceed 100 at most. It's quite impractical to set the value above 100 on most machines, especially when your Job takes a long time to execute org.quartz.threadPool.threadCount = 5 #The priority of a thread. A thread with a higher priority has priority over a thread with a lower priority. The minimum is 1, the maximum is 10, and the default is 5 org.quartz.threadPool.threadPriority = 5 #A class that implements the org.quartz.spi.threadPool interface. The thread pool implementation class of Quartz is org.Quartz.simplethreadpool org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool #=============================================================== #Configure JobStore job storage settings #=============================================================== #Job s are stored in memory by default, which is the following configuration org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore #=============================================================== #Configure Plugins plug in configuration #=============================================================== org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin org.quartz.plugin.jobInitializer.overWriteExistingJobs = true org.quartz.plugin.jobInitializer.failOnFileNotFound = true org.quartz.plugin.jobInitializer.validating=false
You can also write program code to operate the contents of the quartz.properties file
// Create factory instance StdSchedulerFactory schedulerFactory = new StdSchedulerFactory(); // Create an object that configures the properties of the factory Properties prop = new Properties(); prop.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool"); prop.put("org.quartz.threadPool.threadCount", "5"); try { // Load the properties defined above schedulerFactory.initialize(prop); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.start(); } catch (SchedulerException e) { e.printStackTrace(); }
3, Quartz monitor
1. Concept
The listener of quartz is used to get the notification of the event in time when the event you pay attention to occurs in the task scheduling. It is similar to the reminder of email and SMS in the process of task execution. Quartz listeners are mainly composed of JobListener, TriggerListener and SchedulerListener. As the name suggests, they are distributed to represent the listeners corresponding to tasks, triggers and schedulers. Before introducing the three kinds of listeners, we need to make clear two concepts: Global listener and non global listener
- The global listener can receive all Job/Trigger event notifications
- The non global listener can only receive the events of the Job or Trigger registered on it, and the Job or Trigger not registered on it will not listen.
This course will introduce the use of global and non global listeners one by one.
2,JobListener
In the process of task scheduling, the events related to job include: the prompt of job start to execute; Job execution completion prompt, etc.
public interface JobListener { public String getName(); public void jobToBeExecuted(JobExecutionContext context); public void jobExecutionVetoed(JobExecutionContext context); public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException); }
Among them:
. getName method: used to get the name of the JobListener.
. jobToBeExecuted method: the Scheduler calls this method when JobDetail is about to be executed.
. jobExecutionVetoed method: the Scheduler will call this method when the JobDetail is about to be executed, but it is rejected by the TriggerListener.
jobWasExecuted method: Scheduler calls this method after JobDetail is executed.
Example:
HelloJobListener.java
// Define task class public class HelloJobListener implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // Output current time Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = dateFormat.format(date); // job content System.out.println("The database is being backed up at the following time:" +dateString); } }
Create a custom JobListener
MyJobListener.java
public class MyJobListener implements JobListener { @Override public String getName() { String name = this.getClass().getSimpleName(); System.out.println("The name of the listener is:" +name); return name; } @Override public void jobToBeExecuted(JobExecutionContext context) { String name = context.getJobDetail().getKey().getName(); System.out.println("Job The name of the project is:" +name + " Scheduler stay JobDetail The method to be called when it is about to be executed"); } @Override public void jobExecutionVetoed(JobExecutionContext context) { String name = context.getJobDetail().getKey().getName(); System.out.println("Job The name of the project is:" +name + " Scheduler stay JobDetail To be executed, but to be executed TriggerListener The method is called when it is vetoed"); } @Override public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { String name = context.getJobDetail().getKey().getName(); System.out.println("Job The name of the project is:" +name + " Scheduler stay JobDetail This method is called after execution."); } }
Execution scheduler
HelloSchedulerDemoJobListener.java
public class HelloSchedulerDemoJobListener { public static void main(String[] args) throws Exception { // 1. Scheduler to obtain the instance of the schedule from the factory (default: instantiate new StdSchedulerFactory();) Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 2. The Job detail defines a task scheduling instance, which is bound to hellojob simpletrigger. The Job class needs to implement the Job interface JobDetail jobDetail = JobBuilder.newJob(HelloJobListener.class) // Load the task class, complete the binding with HelloJob, and require HelloJob to implement the Job interface .withIdentity("job1", "group1") // Parameter 1: the name of the task (unique instance); Parameter 2: the name of the task group .build(); // 3. Trigger defines a trigger, executes immediately, and then repeats every 5 seconds Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") // Parameter 1: name of trigger (unique instance); Parameter 2: the name of the trigger group .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5).withRepeatCount(2)) // It is executed every 5 seconds and stops after 3 consecutive times. The default value is 0 .build(); // 4. Let the scheduler associate tasks and triggers to ensure that tasks are executed according to the adjustment defined by triggers scheduler.scheduleJob(jobDetail, trigger); // Create and register a global Job Listener // scheduler.getListenerManager().addJobListener(new MyJobListener(), EverythingMatcher.allJobs()); // Create and register a local Job Listener to represent the specified Job scheduler.getListenerManager().addJobListener(new MyJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("job1", "group1"))); // 5. Start up scheduler.start(); // close //scheduler.shutdown(); } }
3,TriggerListener
In the process of task scheduling, Trigger trigger related events include: Trigger triggered, Trigger not triggered correctly, Trigger completed, etc.
public interface TriggerListener { public String getName(); public void triggerFired(Trigger trigger, JobExecutionContext context); public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context); public void triggerMisfired(Trigger trigger); public void triggerComplete(Trigger trigger, JobExecutionContext context, CompletedExecutionInstruction triggerInstructionCode) }
Among them:
. getName method: used to get the name of the trigger.
. triggerFired method: when the Trigger associated with the listener is triggered and the Execute() method on the Job will be executed, the Scheduler calls this method.
. vetojoboxecution method: after Trigger, the Scheduler calls this method when the Job is about to be executed. TriggerListener gives you an option to veto Job execution. If this method returns true, the Job will not be executed for this Trigger.
. Trigger misfire method: the Scheduler calls this method when the Trigger misses the Trigger. You should pay attention to the long-lasting logic in this method: when there are many triggers that miss the Trigger, the long logic will lead to the domino effect. You should keep this method as small as possible.
. triggerComplete method: when Trigger is triggered and Job execution is completed, the Scheduler calls this method.
Example:
The following example briefly shows the use of TriggerListener, where creating and registering a TriggerListener is almost similar to a JobListener.
HelloJobListener.java
// Define task class public class HelloJobListener implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // Output current time Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = dateFormat.format(date); // job content System.out.println("The database is being backed up at the following time:" +dateString); } }
MyTriggerListener.java
public class MyTriggerListener implements TriggerListener { private String name; // Construct a method to customize the name of the pass trigger. The default is the name of the class public MyTriggerListener(String name) { super(); this.name = name; } @Override public String getName() { return this.name; // No return will throw an exception with a null name } // @Override // public String getName() { // String name = this.getClass().getSimpleName(); // System.out.println("trigger name: + name)"; // return name; // No return will throw an exception with a null name // } @Override public void triggerFired(Trigger trigger, JobExecutionContext context) { String name = this.getClass().getSimpleName(); System.out.println(name +"Triggered"); } @Override public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { String name = this.getClass().getSimpleName(); // TriggerListener gives you an option to veto Job execution. If this method returns true, the Job will not be executed for this Trigger. System.out.println(name +" It's not triggered"); return false; // true: indicates that the Job method will not be executed } @Override public void triggerMisfired(Trigger trigger) { String name = this.getClass().getSimpleName(); // The Scheduler calls this method when Trigger is missed System.out.println(name +" Miss trigger"); } @Override public void triggerComplete(Trigger trigger, JobExecutionContext context, CompletedExecutionInstruction triggerInstructionCode) { String name = this.getClass().getSimpleName(); // When Trigger is triggered and Job execution is completed, the Scheduler calls this method. System.out.println(name +" Trigger after completion"); } }
HelloSchedulerDemoTriggerListener.java
public class HelloSchedulerDemoTriggerListener { public static void main(String[] args) throws Exception { // 1. Scheduler to obtain the instance of the schedule from the factory (default: instantiate new StdSchedulerFactory();) Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 2. The Job detail defines a task scheduling instance, which is bound to hellojob simpletrigger. The Job class needs to implement the Job interface JobDetail jobDetail = JobBuilder.newJob(HelloJobListener.class) // Load the task class, complete the binding with HelloJob, and require HelloJob to implement the Job interface .withIdentity("job1", "group1") // Parameter 1: the name of the task (unique instance); Parameter 2: the name of the task group .build(); // 3. Trigger defines a trigger, executes immediately, and then repeats every 5 seconds Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") // Parameter 1: name of trigger (unique instance); Parameter 2: the name of the trigger group .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5).withRepeatCount(2)) // It is executed every 5 seconds and stops after 3 consecutive times. The default value is 0 .build(); // 4. Let the scheduler associate tasks and triggers to ensure that tasks are executed according to the adjustment defined by triggers scheduler.scheduleJob(jobDetail, trigger); // Create and register a global Trigger Listener // scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(), EverythingMatcher.allTriggers()); // Create and register a local Trigger Listener scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1", "group1"))); // 5. Start up scheduler.start(); // close //scheduler.shutdown(); } }
4,SchedulerListener
SchedulerListener is called when key events occur in the life cycle of the Scheduler. Events related to Scheduler include: adding a Job/Trigger, deleting a Job/Trigger, serious error in Scheduler, closing Scheduler, etc.
public interface SchedulerListener { public void jobScheduled(Trigger trigger); public void jobUnscheduled(TriggerKey triggerKey); public void triggerFinalized(Trigger trigger); public void triggersPaused(String triggerGroup); public void triggersResumed(String triggerGroup); public void jobsPaused(String jobGroup); public void jobsResumed(String jobGroup); public void schedulerError(String msg, SchedulerException cause); public void schedulerStarted(); public void schedulerInStandbyMode(); public void schedulerShutdown(); public void schedulingDataCleared() }
Among them:
. jobScheduled method: used to deploy JobDetail.
. jobUnscheduled method: used to unload JobDetail.
. triggerFinalized method: this method is called when a Trigger comes to a state that will never be triggered again. Unless the Job is persistent, it will be removed from the Scheduler.
. triggersPaused method: the Scheduler calls this method when a Trigger or Trigger group is suspended. If it is a Trigger group, the Trigger name parameter will be null.
. triggersResumed method: the Scheduler calls this method when a Trigger or Trigger group resumes from a pause. If it is a Trigger group, the Trigger name parameter will be null.
. jobsPaused method: this method is called when one or a group of jobdetails is suspended.
. jobsResumed method: this method is called when one or a group of jobs resume from the pause. If it is a Job group, the jobName will be null.
. schedulerError method: this method is called when a serious error occurs during the normal operation of the Scheduler.
. schedulerStarted method: this method is called when the Scheduler is on.
. schedulerInStandbyMode method: this method is called when the Scheduler is in standbymode.
. schedulerShutdown method: this method is called when the Scheduler stops.
. schedulingDataCleared method: this method is called when the data in the Scheduler is cleared.
Example:
The following code briefly describes how to use the SchedulerListener method:
HelloJobListener.java
// Define task class public class HelloJobListener implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // Output current time Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = dateFormat.format(date); // job content System.out.println("The database is being backed up at the following time:" +dateString); } }
MySchedulerListener.java
public class MySchedulerListener implements SchedulerListener { @Override public void jobScheduled(Trigger trigger) { String name = trigger.getKey().getName(); // Used to deploy JobDetail System.out.println(name +" Complete the deployment"); } @Override public void jobUnscheduled(TriggerKey triggerKey) { String name = triggerKey.getName(); // Used to unload JobDetail System.out.println(name +" Complete the uninstall"); } @Override public void triggerFinalized(Trigger trigger) { String name = trigger.getKey().getName(); // This method is called when a Trigger comes to a state that will never be triggered again. Unless the Job is persistent, it will be removed from the Scheduler. System.out.println(name +" Trigger removed"); } @Override public void triggerPaused(TriggerKey triggerKey) { String name = triggerKey.getName(); // The Scheduler calls this method when a Trigger or Trigger group is suspended. If it is a Trigger group, the Trigger name parameter will be null. System.out.println(name +" Being suspended"); } @Override public void triggersPaused(String triggerGroup) { // The Scheduler calls this method when a Trigger or Trigger group is suspended. If it is a Trigger group, the Trigger name parameter will be null. System.out.println("Trigger group" +triggerGroup +" Being suspended"); } @Override public void triggerResumed(TriggerKey triggerKey) { // The Scheduler calls this method when a Trigger or Trigger group resumes from a pause. If it is a Trigger group, the Trigger name parameter will be null. The parameter will be null. String name = triggerKey.getName(); System.out.println(name +" Resuming from pause"); } @Override public void triggersResumed(String triggerGroup) { // The Scheduler calls this method when a Trigger or Trigger group resumes from a pause. If it is a Trigger group, the Trigger name parameter will be null. The parameter will be null. System.out.println("Trigger group" +triggerGroup +" Resuming from pause"); } @Override public void jobAdded(JobDetail jobDetail) { // System.out.println(jobDetail.getKey() +" Add task"); } @Override public void jobDeleted(JobKey jobKey) { // System.out.println(jobKey +" Delete work task"); } @Override public void jobPaused(JobKey jobKey) { // System.out.println(jobKey +" The task is being suspended"); } @Override public void jobsPaused(String jobGroup) { // System.out.println("Working group" +jobGroup +" Being suspended"); } @Override public void jobResumed(JobKey jobKey) { // System.out.println(jobKey +" Resuming from pause"); } @Override public void jobsResumed(String jobGroup) { // System.out.println("Working group" +jobGroup +" Resuming from pause"); } @Override public void schedulerError(String msg, SchedulerException cause) { // This method is called when a serious error occurs during the normal operation of the Scheduler. System.out.println("Called when a serious error occurs" +msg +" " +cause.getUnderlyingException()); } @Override public void schedulerInStandbyMode() { // This method is called when the Scheduler is in StandBy mode. System.out.println("Called when the scheduler is suspended"); } @Override public void schedulerStarted() { // This method is called when the Scheduler is on System.out.println("Called when the scheduler is on"); } @Override public void schedulerStarting() { // System.out.println("Called when the scheduler is turning on"); } @Override public void schedulerShutdown() { // System.out.println("Called when the scheduler is closed"); } @Override public void schedulerShuttingdown() { // System.out.println("Called when the scheduler is shutting down"); } @Override public void schedulingDataCleared() { // This method is called when the data in the Scheduler is cleared System.out.println("Called when scheduler data is cleared"); } }
HelloSchedulerDemoTriggerListener.java
public class HelloSchedulerDemoTriggerListener { public static void main(String[] args) throws Exception { // 1. Scheduler to obtain the instance of the schedule from the factory (default: instantiate new StdSchedulerFactory();) Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 2. The Job detail defines a task scheduling instance, which is bound to hellojob simpletrigger. The Job class needs to implement the Job interface JobDetail jobDetail = JobBuilder.newJob(HelloJobListener.class) // Load the task class, complete the binding with HelloJob, and require HelloJob to implement the Job interface .withIdentity("job1", "group1") // Parameter 1: the name of the task (unique instance); Parameter 2: the name of the task group .build(); // 3. Trigger defines a trigger, executes immediately, and then repeats every 5 seconds Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") // Parameter 1: name of trigger (unique instance); Parameter 2: the name of the trigger group .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5).withRepeatCount(2)) // It is executed every 5 seconds and stops after 3 consecutive times. The default value is 0 .build(); // 4. Let the scheduler associate tasks and triggers to ensure that tasks are executed according to the adjustment defined by triggers scheduler.scheduleJob(jobDetail, trigger); // Creating a listener for the scheduler scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener()); // Remove the listener of the corresponding scheduler // scheduler.getListenerManager().removeSchedulerListener(new MySchedulerListener()); // 5. Start up scheduler.start(); // Thread shut down after 7 seconds delay Thread.sleep(7000L); // close scheduler.shutdown(); } }
4, Persistent to Mysql
1. Download sql file
There are sql files in the original code of Quartz
- https://github.com/quartz-scheduler/quartz/blob/v2.3.2/quartz-core/src/main/resources/org/quartz/impl/jdbcjobstore/tables_mysql.sql
- Another address: https://hub.fastgit.org/quartz-scheduler/quartz/tree/master/quartz-core/src/main/resources/org/quartz/impl/jdbcjobstore
Download and import it into the database. I use mysql5.7 here
Table name | describe |
---|---|
QRTZ_BLOB_TRIGGERS | Store as Blob type (used when Quartz users create their own custom Trigger type with JDBC, and JobStore doesn't know how to store instances) |
QRTZ_CALENDARS | Store Calendar information of Quartz in Blob type |
QRTZ_CRON_TRIGGERS | Store Cron Trigger, including Cron expression and time zone information |
QRTZ_FIRED_TRIGGERS | Store status information related to triggered Trigger and execution information of associated Job |
QRTZ_JOB_DETAILS | Store the details of each configured Job |
QRTZ_LOCKS | Store the program's non watch lock information (if pessimistic lock is used) |
QRTZ_PAUSED_TRIGGER_GRPS | Stores information about the pending Trigger group |
QRTZ_SCHEDULER_STATE | Store a small amount of state information about the Scheduler, and other instances of the Scheduler (if used in a cluster) |
QRTZ_SIMPLE_TRIGGERS | Store a simple Trigger, including the number of repetitions, intervals, and the number of touches |
QRTZ_SIMPROP_TRIGGERS | |
QRTZ_TRIGGERS | Stores information about the configured Trigger |
2. Introduce dependency
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.zyx</groupId> <artifactId>quartz</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <exclusions> <exclusion> <!--Exclude the self-contained JDBC Connection pool--> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--Timed tasks need to rely on context modular--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!--Connect to database--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--The following package can be replaced with mybatis perhaps mybatisPlus--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> </dependencies>
3. Configure SchedulerFactory
To configure a data source:
spring: datasource: username: root password: root url: jdbc:mysql://10.211.55.12:3306/test driver-class-name: com.mysql.cj.jdbc.Driver
To configure SchedulerFactory:
@Configuration public class ScheduleConfig { @Bean public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setDataSource(dataSource); // quartz parameter Properties prop = new Properties(); prop.put("org.quartz.scheduler.instanceName", "ZyxScheduler"); prop.put("org.quartz.scheduler.instanceId", "AUTO"); //If cluster is used, instanceId must be unique and set to AUTO // Thread pool configuration prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); prop.put("org.quartz.threadPool.threadCount", "20"); //Number of threads prop.put("org.quartz.threadPool.threadPriority", "5"); //priority // JobStore configuration prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX"); //Configure usage database // Cluster configuration prop.put("org.quartz.jobStore.isClustered", "true"); //Is it cluster mode prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); // Enable SQL Server // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); prop.put("org.quartz.jobStore.misfireThreshold", "12000"); prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); //Database table prefix factory.setQuartzProperties(prop); factory.setSchedulerName("DesScheduler"); // Delayed start factory.setStartupDelay(1); factory.setApplicationContextSchedulerContextKey("applicationContextKey"); // Optional, QuartzScheduler // Update existing jobs at startup, so that you don't need to delete qrtz after modifying targetObject every time_ Job_ The details table corresponds to the record factory.setOverwriteExistingJobs(true); // Set auto start, the default is true factory.setAutoStartup(true); return factory; } }
4. Use custom Scheduler
A simple Job:
@Data public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println(new Date()); } }
Use a custom Scheduler:
@SpringBootTest class QuartzApplicationTests { //Inject the above configured factoryBean @Autowired private SchedulerFactoryBean factoryBean; @Test void contextLoads() throws SchedulerException, InterruptedException { Scheduler scheduler = factoryBean.getScheduler(); scheduler.clear(); //2. Task instance (JobDetail) JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "jobGroup1") .build(); //3. Trigger Trigger trigger = TriggerBuilder.newTrigger() .startNow() .withIdentity("trigger1", "triggerGroup1") .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()) .build(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); Thread.sleep(100000); } }
5. View the database
Looking at the database, you can find that the relevant data in Quartz has been saved to the database
6. Start the Scheduler again
Let the program run directly, without creating a new timing task, you will find that the timing task just saved in the database will be executed automatically
@SpringBootTest class QuartzApplicationTests { @Autowired private SchedulerFactoryBean factoryBean; @Test void contextLoads() throws SchedulerException, InterruptedException { Thread.sleep(100000); } }
#Use your own profile org.quartz.jobStore.useProperties=true #Default or change your name org.quartz.scheduler.instanceName= DefaultQuartzScheduler #If cluster is used, instanceId must be unique and set to AUTO org.quartz.scheduler.instanceId = AUTO org.quartz.threadPool.class= org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount= 10 org.quartz.threadPool.threadPriority= 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread= true #The storage method uses JobStoreTX, that is, database org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate #Whether to use cluster (if the project is deployed to only one server, it is unnecessary) org.quartz.jobStore.isClustered = false org.quartz.jobStore.clusterCheckinInterval=20000 org.quartz.jobStore.tablePrefix = qrtz_ org.quartz.jobStore.dataSource = test #Configure data sources #Table name prefix of quartz table in database org.quartz.dataSource.test.driver = com.mysql.jdbc.Driver org.quartz.dataSource.test.URL = jdbc:mysql://localhost:3306/test?serverTimezone=GMT&characterEncoding=utf-8 org.quartz.dataSource.test.user = root org.quartz.dataSource.test.password = root org.quartz.dataSource.test.maxConnections = 5