One article to learn the use of Quartz common API

In the Introduction and Getting Started with Task Scheduling Service Quartz, we briefly introduce what Quartz is and how to use Quartz. In this article, we introduce the commonly used APIs of Quartz. These APIs will run through the entire process of using Quartz. First, we introduce the use of SchedulerFactory.

The official website explains ScheduleFactory in this way, Provides a mechanism for obtaining client-usable handles to Scheduler instances. Before using the scheduler (Scheduler), the scheduler needs to be instantiated. You can use SchedulerFactory to create schedulers. SchedulerFactory has two implementation classes, StdSchedulerFactory and DirectSchedulerFactory are explained separately below. Some use Factory instances directly for instantiation, and some Quartz users use Factory instances in JNDI. The ScheduleFactory class diagram is as follows:

DirectSchedulerFactory is a singleton implementation of SchedulerFactory. DirectSchedulerFactory is a protected mode, we cannot create a new instance through the keyword new. DirectSchedulerFactory provides the getInstance() method to obtain an instance.

 public  void  createVolatileScheduler( int maxThreads ) is used to create a scheduler (Scheduler) that has nothing to do with the database. It uses RAMJobStore to store scheduler information. And the scheduler name is SimpleQuartzScheduler, and the instance Id is SIMPLE_NON_CLUSTERED. The code example is as follows:

public class DirectVolatileSchedulerTest {
    public static void main(String[] args) throws SchedulerException {
        DirectSchedulerFactory.getInstance().createVolatileScheduler(10);
	Scheduler scheduler = DirectSchedulerFactory.getInstance().getScheduler();
	scheduler.start();
	System.out.println(scheduler);
        cheduler.shutdown();
    }
}

public  void  createScheduler(ThreadPool threadPool, JobStore jobStore) createScheduler is an overloaded method, we can use this method to create a suitable scheduler. The specific method can be found in the official API document (http://www.quartz-scheduler.org/api/2.4.0-SNAPSHOT/index.html).

public class DirectCreateSchedulerTest {
    public static void main(String[] args) throws SchedulerException {
        DirectSchedulerFactory diSchedulerFactory = DirectSchedulerFactory.getInstance();
        ThreadPool threadPool = new SimpleThreadPool(10,5);
	JobStore jobStore = new RAMJobStore();
	diSchedulerFactory.createScheduler("My Quartz Scheduler", "My Instance", threadPool, jobStore); 
	Scheduler scheduler = diSchedulerFactory.getScheduler("My Quartz Scheduler");
	scheduler.start();
	System.out.println(scheduler);
	scheduler.shutdown();
    }
}

In the above example, we used RAMJobStore, we can also use JDBCJobStore as JobStore to store scheduler information. Follow-up Job Stores will explain in more detail.

DBConnectionManager.getInstance().addConnectionProvider("someDatasource", new JNDIConnectionProvider("someDatasourceJNDIName")); 
JobStoreTX jdbcJobStore = new JobStoreTX(); 
jdbcJobStore.setDataSource("someDatasource"); 
jdbcJobStore.setPostgresStyleBlobs(true); 
jdbcJobStore.setTablePrefix("QRTZ_"); 
jdbcJobStore.setInstanceId("My Instance");

public void createRemoteScheduler(String rmiHost, int rmiPort) creates a proxy for a remote scheduler, which can be obtained through directschedulerFactory.getScheduler(). DirectSchedulerFactory provides getScheduler method and getAllSchedulers to obtain scheduler. The public Scheduler getScheduler() method gets the scheduler whose scheduler name is SimpleQuartzScheduler; the public Scheduler getScheduler(String schedName) method gets the scheduler whose scheduler name is scheName; public Collection<Scheduler> getAllSchedulers() gets all schedulers.

StdSchedulerFactory is another implementation of SchedulerFactory. It creates a QuartzScheduler instance through the Properties file. You can create an instance of SchedulerFactory through the constructor. By default, the file named "quartz.properties" in the current working directory will be automatically loaded. If the loading fails, the file located in the org/quartz package will be loaded. To use files other than these default values, the system property "org.quartz.properties" must be defined to point to the required file. When configuring quartz.properties, at least org.quartz.threadPool.threadCount must be configured. The value is an integer greater than 0, otherwise an exception will be thrown. The contents of the quartz.properties file will be explained in detail in the configuration section. The following is the construction method of StdSchedulerFactory, you can create a SchedulerFactory instance through the construction method:

/创建一个未初始化的StdSchedulerFactory。再调用getScheduler()方法时,会判断是否初始化,如果
//未初始化则会先初始化。初始化所使用的的配置文件如上面所述。
public StdSchedulerFactory() {
}
//使用自己创建的Properties实例而非配置文件创建StdSchedulerFactory。
public StdSchedulerFactory(Properties props) throws SchedulerException {
    initialize(props);
}
//传入文件名称,使用指定的文件创建StdSchedulerFactory
public StdSchedulerFactory(String fileName) throws SchedulerException {
    initialize(fileName);
}

StdSchedulerFactory will create a scheduler when calling getScheduler(); you can also initialize it by calling the initialize(xx) method before calling GetScheduler().

public Scheduler getScheduler() throws SchedulerException {
    //如果配置为空,初始化配置
    if (cfg == null) {
        initialize();
    }
    //调度器资源中获取调度器
    SchedulerRepository schedRep = SchedulerRepository.getInstance();
    Scheduler sched = schedRep.lookup(getSchedulerName());
    //如果获取到调度器
    if (sched != null) {
        //如果调度器已关闭
        if (sched.isShutdown()) {
            //移除
            schedRep.remove(getSchedulerName());
        } else {
            //否则,返回
            return sched;
        }
    }
    //否则,初始化调度器
    sched = instantiate();
    return sched;
}

DirectSchedulerFactory is a singleton implementation of SchedulerFactory. It is useful for users who want to create their Scheduler instance in a more programmatic way. However, its usage is generally discouraged, because this method will eventually write the configuration into the code in a hard-coded manner. When modifying, only the code can be modified. This operation is undoubtedly complicated. StdSchedulerFactory is another implementation of the org.quartz.SchedulerFactory interface. It uses a set of properties (java.util.Properties) to create and initialize the Quartz Scheduler. Properties are usually loaded from the quartz.properties file by default, but they can also be created by the program and passed directly to StdSchedulerFactory, or you can specify the properties file to load. Calling getScheduler() in the factory will generate the scheduler, initialize it (and its ThreadPool, JobStore and DataSources) and return an instance to its public interface.

The main API for interacting with the scheduler is Scheduler. From the above, we can see that we use SchedulerFactory to create Scheduler instances. Its life cycle starts when the SchedulerFactory creates it and ends when the Scheduler calls the shutdown() method; after the Scheduler is created, you can add, delete, and enumerate Jobs and Triggers, and perform other scheduling-related operations (such as suspending Triggers) . However, the Scheduler will only actually trigger the trigger (ie, execute the job) after calling the start() method. Note: After the scheduler is stopped, it cannot be restarted unless it is re-instantiated; only when the scheduler is started, even if it is in a suspended state, the trigger will be triggered (the job will be executed). The Scheduler class diagram is as follows:

The scheduler is responsible for maintaining JobDetail and Trigger. Once they are registered with the Scheduler, the scheduler will be responsible for executing the jobs associated with them. Register jobs and triggers to Scheduler by the following method. Date scheduleJob(JobDetail jobDetail , Trigger trigger ) throws  SchedulerException;

The JobDetail mentioned above needs to define a job task, which needs to implement the Job interface. The interface is very simple and simple. There is a total of methods in it. The method that implements the interface is generally called the job class and is used to complete the job:

package org.quartz;
public interface Job {
    void execute(JobExecutionContext context) throws JobExecutionException;
}

Our job class needs to define its own class to implement this interface and implement the execute method. This class only indicates what type of tasks the job needs to complete. Quartz also needs to know the attributes contained in the Job instance; in the above we defined the scheduler and job tasks, but we do not directly instantiate the job to the scheduler to execute, but through the JobDetail instance, and give the job class Add additional instance information. JobDetail instances are generally created through JobBuilder.

JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("myJob", "group1").build();
  1.   The withDescription method is used to describe the information of this job and what the job class is used for.
  2.   WithIdentity method, the job class will be assigned a default group and a default name to identify the job class by default. We can use the withIdentity method to specify the group and name for the job class. It is an overloaded method that can easily specify a group or name for the job class.
  3.   UsingJobData or setJobData In the job class, you should not define stateful data attributes, because the values ​​of these attributes will not be retained during multiple executions of the job. So how to give job instance. How about adding attributes or configuration? How to track the status of the job during multiple executions of the job? The answer is: JobDataMap, part of the JobDetail object. You can use the usingJobData or setJobData method to set. usingJobData is also an overloaded method. It is very convenient to set the attribute value. The value of obDataMap is immutable. If you want to change the value every time you execute a task, you can add an annotation to the job class: @PersistJobDataAfterExecution , and @DisallowConcurrentExecution tells Quartz not to execute the same job definition concurrently (here refers to a specific job Class).
  4.   In the requestRecovery method, when a Sheduler instance fails to execute a job, it is possible that another working Scheduler instance will take over the job and run it again. To achieve this behavior, the Job recoverable property configured to the JobDetail object must be set to true (job.setRequestsRecovery(true)). If the recoverable property is set to false (default is false), when a Scheduler fails to run the job, it will not rerun; instead, it will be triggered by another Scheduler instance at the next trigger time. How quickly the Scheduler instance fails to be detected depends on the check-in interval of each Scheduler (org.quartz.jobStore.clusterCheckinInterval)
  5.   By default in storeDurably method, when the job does not have a corresponding Trigger, the job cannot be directly added to the scheduler, or after the Trigger expires, the job without associated Trigger will also be deleted. We can store Job independently in the scheduler through StoreDurably of JobBuilder.

Earlier we also mentioned Trigger, Trigger is trigger, Trigger is used to define the conditions of job execution, that is, when to trigger the scheduler to start executing the job. Trigger instances are created by the TriggerBuilder class, which can add additional instance information to Trigger.

Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever()).build();
  1. The withDescription method is used to describe the information of this trigger and what the trigger is used for.
  2. WithIdentity method By default, the trigger will assign a default group and a default name to identify the trigger. We can use the withIdentity method to specify the group and name for the trigger. It is an overloaded method that can easily specify a group or name for the trigger.
  3. The startAt method sets the time when the trigger should start-the trigger may not start at this time-depending on the plan configured. Trigger (configured in the withSchedule method). However, the trigger will not fire until then, regardless of the trigger schedule.
  4. The startNow method sets the time when the trigger should start to the current moment-the trigger may or may not fire at this time-depending on the schedule configured for the trigger (configured in the withSchedule method). The default is startNow
  5. The endAt method sets the time when the trigger will no longer fire-even if it is scheduled to have repetitions left.
  6. withPriority If you have a lot of triggers (or too few worker threads in the Quartz thread pool), Quartz may not have enough resources to trigger all triggers at the same time; in this case, you may want to control which triggers use .Quartz worker threads first. To achieve this goal, you can set the priority attribute on the trigger. For example, if you have N triggers that need to be triggered at the same time, but only Z worker threads, the Z triggers with the highest priority will be triggered first. If the priority is not set for the trigger, the trigger uses the default priority, which is 5; the value of the priority attribute can be any integer, positive or negative. Note : Only the triggers that are triggered at the same time will compare the priority. The trigger triggered at 10:59 is always executed before the trigger triggered at 11:00. Note : If the trigger is recoverable, the priority is the same as the original trigger when scheduling after recovery.
  7. The usingJobData method adds additional data to the trigger, the usage is similar to the usage in Job
  8. The forJob method sets an identifier of which trigger should trigger the job. Specify by the specified JobKey.
  9. The modifiedByCalendar method sets the Calendar trigger to fire.
  10. The withSchedule method settings will be used to define the schedule of the trigger. Define the specific types of knowledge triggers and how to execute different triggers. The following figure shows the class relationship diagram of the trigger type

 We use ScheduleBuilder to create different triggers. The following figure shows the class relationship diagrams of different Builders.

 

ScheduleBuilder<SimpleTrigger> schedBuilder =SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever();
Trigger trigger = newTrigger().withIdentity("start_trigger", "start_group").startNow()
.withSchedule(schedBuilder).build();

There are many different ways to create the triggers you need under different Triggers. You can check the Quartz API documentation for specific methods. We only need to know that there are these triggers, and when to use which is more convenient.

Quartz provides a listener interface for performing operations based on events that occur in the scheduler. Quartz provides three listeners related to the scheduler. TriggerListener, JobListener and SchedulerListener.

TriggerListeners receives events related to triggers. Events related to triggers include: trigger trigger, trigger failure, and trigger completion (job completion of trigger closed). The following is the method and explanation of the interface.

  1. String getName();   Get the name of TriggerListener
  2. void triggerFired(Triggertrigger, JobExecutionContextcontext);Called when Trigger is fired. The Job associated with it is about to be executed, and it is called before the VoteJobExecution(...) method is called.
  3. boolean  vetoJobExecution(Triggertrigger, JobExecutionContextcontext); Trigger is called when triggered, and the Job associated with it is about to be executed. If the implementation of this method returns True, then the Job will not be called. Itis called aftertriggerFired(...)
  4. void  triggerMisfired(Triggertrigger); Execute when the trigger miss is fired. For example, there are many triggers that need to be executed at the current time, but the effective threads in the thread pool are all working, then the trigger may time out and miss this round of triggering. You should consider how long this method will take to execute, because it will affect all triggers that are not fired. Once you have a large number of triggers that should be fired that are not fired, this method can help you solve many problems.
  5. void  triggerComplete(Triggertrigger, JobExecutionContextcontext, int  triggerInstructionCode);Start  when the task is completed.

JobListeners receives events related to the job (Job). Events related to the job include: the notification that the job is about to be executed, and the notification when the job is completed. The following is the method and explanation of the interface.

  1. String getName();  Get the name of JobListener
  2. void  jobToBeExecuted(JobExecutionContextcontext);This method will be executed when the job is executed. This method will not be executed. When the job is rejected by TriggerListener,
  3. void  jobExecutionVetoed(JobExecutionContextcontext); is called when the Job is rejected by TriggerListener
  4. Void jobWasExecuted(JobExecutionContextcontext, JobExecutionExceptionjobException);is called when the job has been executed

SchedulerListeners are very similar to TriggerListeners and JobListeners, except that they receive notifications of events in the Scheduler itself-not necessarily events related to a particular trigger or job. Events related to the scheduler include: adding a job/trigger, deleting a job/trigger, serious errors in the scheduler, notification of closing the scheduler, etc.

  1. void  jobScheduled(Triggertrigger); Thecall is executed when the job is scheduled
  2. void  jobUnscheduled (TriggerKeytriggerKey);the Job is canceled when the scheduled call
  3. void  triggerFinalized(Triggertrigger); Called whenTrigger becomes a state that will never be triggered
  4. void  triggerPaused (TriggerKeytriggerKey);pause triggerwhen calling
  5. void  triggersPaused(StringtriggerGroup);called when thetriggerispaused
  6. void  triggerResumed (TriggerKeytriggerKey);recovery triggerswhen calls
  7. void  triggersResumed(StringtriggerGroup);called when thetriggerisrestored
  8. void  jobAdded(JobDetailjobDetail);called whenadding ajob
  9. void  jobDeleted(JobKeyjobKey);called whendeleting ajob
  10. void  jobPaused(JobKeyjobKey);called when the job ispaused
  11. void  jobsPaused(StringjobGroup);called when the job ispaused
  12. void  jobResumed(JobKeyjobKey);called when the job isresumed
  13. void  jobsResumed(StringjobGroup);called when the job isresumed
  14. void  schedulerError(Stringmsg, SchedulerExceptioncause); Called when a serious error occurs in the scheduler.
  15. void  schedulerInStandbyMode();called when the scheduler switches to standby mode
  16. void  schedulerStarted();called when the scheduler has started
  17. void  schedulerStarting();called when the scheduler is starting
  18. void  schedulerShutdown();called when the scheduler has been shut down
  19. void  schedulerShuttingdown();called when the scheduler starts to shut down
  20. void  schedulingDataCleared();calledwhen allJob, Trigger, Calendar are deleted

To create our own Listener, we only need to create an object that implements the org.quartz.TriggerListener or org.quartz.JobListener or import org.quartz.SchedulerListener interface. The listener will register with the scheduler at runtime. For convenience, the class can also extend JobListenerSupport or TriggerListenerSupport or SchedulerListenerSupport, and only need to cover the events you are interested in. They are abstract classes that implement the above three interfaces. All listeners are registered to the ListenerManager of the scheduler for management.

  1. 添加监听器:sched.getListenerManager().addJobListener(jobListener); sched.getListenerManager().addTriggerListener(triggerListener);sched. getListenerManager().addSchedulerListener(schedulerListener)。
  2. 移除监听器sched.getListenerManager().removeJobListener(jobListener); sched.getListenerManager().removeTriggerListener(triggerListener); sched.getListenerManager().removeedulerListener(schedulerListener);

Most users of Quartz do not use Listeners, but when the application needs to create an event notification, it is very convenient for these users to explicitly notify the application without the Job itself.

JobStore is responsible for tracking all the "work data" you provide to the scheduler: jobs, triggers, etc. Choosing the appropriate JobStore for your Quartz scheduler instance is an important step. Quartz supports data storage and persistence. The following examples are all given in hard-coded form. Generally, we will use configuration files instead of directly hard-coding and specifying JobStore.

 RAMJobStore is the simplest JobStore to use, and it is also the highest performance. As the name suggests, it saves all data in RAM. The disadvantage is that when your application ends (or crashes), all scheduling information will be lost-which means RAMJobStore cannot fulfill the "non-volatile" settings on jobs and triggers . Without any configuration, Quartz uses RAMJobStore by default. In other words, our above examples all use RAMJobStore.

JobStore jobStore = new RAMJobStore();
diSchedulerFactory.createScheduler("My Quartz Scheduler", "My Instance", threadPool, jobStore);

 

Quartz can also store all its data in the database via JDBC. The configuration is a bit more complicated than RAMJobStore, and it is not so fast. But its speed is not enough compared to its advantages. JDBCJobStore can be used with almost any database. You can find the table creation SQL script in the "docs/dbTables" directory of the Quartz release.

After creating the table, before configuring and starting the JDBCJobStore, you have another important decision. You need to determine what type of transaction the application requires. If you don't need to bind scheduling commands (such as adding and deleting triggers) to other transactions, you can manage transactions by using JobStoreTX as the JobStore (this is the most common choice). If you need Quartz to work with other transactions (ie J2EE application servers), then you should use JobStoreCMT-in this case, Quartz will let the application server container manage the transactions.

The last difficulty is to set up a DataSource from which JDBCJobStore can obtain a connection to the database. DataSources are defined in one of several different ways in the Quartz properties. One way is to let Quartz create and manage the DataSource itself-by providing all connection information to the database. Another method is to let Quartz use the DataSource managed by the application server that Quartz is running, by providing the JNDI name of the JDBCJobStore DataSource.

JobStoreTX jobStoreTX = new JobStoreTX();
jobStoreTX.setTablePrefix("QRTZ_");
jobStoreTX.setDataSource("pharos_datasource");

 

TerracottaJobStore provides a means of scaling and robustness without using a database. This means that your database is protected from Quartz's load and all its resources can be saved as the rest of the application. TerracottaJobStore can run clustered or non-clustered, and in either case, provides a storage medium for continuous job data between application restarts because the data is stored in the Terracotta server. Its performance is much better than using the database through JDBCJobStore (about an order of magnitude better), but slower than RAMJobStore.

TerracottaJobStore terracottaJobStore = new TerracottaJobStore();
terracottaJobStore.setTcConfigUrl("localhost:9050");
diSchedulerFactory.createScheduler("My Quartz Scheduler", "My Instance", threadPool, terracottaJobStore);

 

Guess you like

Origin blog.csdn.net/wk19920726/article/details/108844072