http://blog.itpub.net/11627468/viewspace-1774071/
quartz官方提供了15个example,方便开发者理解quartz的用法。现在结合源码对实例的使用进行分析。
参见: http://www.quartz-scheduler.org/documentation/quartz-2.2.x/examples/example1:First Quartz Program
1、实例内容包括:
- 启动Quartz Scheduler
- 等待下一个整点后执行任务
- 退出Quartz Scheduler
2、代码:
查上面代码,HelloJob只是一个很简单的定时任务,实现了Job接口,同时实现了execute方法,然后方法内打印了当前时间。
SimpleExample:
1)首先创建一个Scheduler,参照quartz详解2,此时还没有打开启动quartz的主线程QuartzSchedulerThread的开关。
Example 2 - Simple Triggers
1、实例内容也是Simple Trigger的使用,不一样的是批量的使用。
SimpleTriggerExa
mple:
job3每10秒执行1次,重复10次,一共执行11次。
同样是job3,新增一个trigger来触发它。
因为在版本2.2.1中,是可以支持多个trigger来对应触发同一个JOB的。
job5也可以设置5分钟后执行:
job6设置每40秒执行,一直执行:
job7是可以在scheduler启动后再创建,加入到scheduler中。
job8表示,不需要创建trigger,可以直接触发任务。
example3:Cron Trigger
job1是每20秒执行一次:
Jo2是每2分钟执行一次,在15秒这个点执行。如1分15秒,3分15秒,5分15秒。
JOB3是每2分种的整点执行一次,在上午8点到下午17点之间。
JOB5在每个月1号和15号的上午10点执行:
JOB6在每周一到周五执行,每30秒执行一次。
"0,30 * * ? * MON-FRI"
JOB7在每个周六和周日执行,每30秒执行一次。
"0,30 * * ? * MON-FRI"
Example 4 - Job Parameters and Job State
可以为job设置参数和状态。
@DisallowConcurrentExecution 注解的含义相关于1.8.6版本中的stateful job,就是说同一个trigger在上一次没有执行完成时,下一个不能执行。
比如trigger是30s执行一次,一个任务执行了37秒,那么第二个任务需要等37s后,前一个任务执行后了才可以执行。
那么第二个任务是在37s执行,还是在下一个周期60s的时候执行呢?
@PersistJobDataAfterExecution 代表执行完后的job的数据在jobMap中不共享,一般用于有状态的job...
看一下例子:
关于有状态的JOB。
job1是red,job2是green ,每个job每10秒执行一次,那么假如一个任务需要执行3 5 秒呢?下一次执行在什么时候?
答: 按照 trigger 的规则,第 20 秒和第 30 秒分别会有一次预定的触发执行,但是由于是有状态任务,因此实际不会被触发。在第 35秒第一次任务执行完毕时,Quartz 会把第 20秒 和第 30秒的两次触发作为 misfired job 进行处理。对于 misfired job,Quartz 会查看其 misfire 策略是如何设定的,如果是立刻执行,则会马上启动一次执行,如果是等待下次执行,则会忽略错过的任务,而等待下次(即第 6 分钟)触发执行。
有状态的job如果第一次没有执行完,第二次执行的时间错过了,就会被认为是misfire
doUpdateOfMisfiredTrigger调用CronTriggerImpl的updateAfterMisfire方法。
如果misfire设置为MISFIRE_INSTRUCTION_SMART_POLICY或MISFIRE_INSTRUCTION_FIRE_ONCE_NOW就是马上执行。
如果misfire设置为MISFIRE_INSTRUCTION_DO_NOTHING则在下一个周期再执行。
默认是:MISFIRE_INSTRUCTION_SMART_POLICY
可以通过trigger的build方法增加参数实现:
withMisfireHandlingInstructionFireAndProceed().build()
withMisfireHandlingInstructionDoNothing().build()
example5: Job Misfire
JOB1:每3秒执行一次,执行的任务是延时10秒的。statefulJob是有状态的,上一个任务没执行完,是不会执行的。
JOB2:对于misfire的任务设置为马上执行。默认是smart智能执行。在不同的情况下是不一样的。在Cron情况下都是立即执行。
对于SimpleTriggerImpl的
MISFIRE_INSTRUCTION_SMART_POLICY
对于
对于CronTriggerImpl的
MISFIRE_INSTRUCTION_SMART_POLICY
example6 : Dealing with Job Exceptions
在JOB1中如果出现异常了,可以通过抛出异常的方式来重新执行一次。
在JOB2中如果出现异常了,可以终止这个任务,以后也不再执行。
example7 :Interrupting Jobs
JOB实现InterruptableJob接口,实现中断逻辑。
JOB在for循环中,可以被中断执行。
主程序trigger每5秒触发执行。每7秒触发中断。
由代码sched.interrupt()看出来中断只针对当前sched实例,不是针对集群的。如果是集群,需要每个实例都触发。
example8 :Fun with Calendars
可以设置一年内的某几天不执行。
example9
:Job Listeners
通过监听器可以用于日志、报表的处理。
也可以通过JOB监听器实现在JOB执行前后调用其它JOB,实现简单的任务流程。
监听器需要实现接口JobListener,可以实现jobExecutionVetoed,jobToBeExecuted,jobWasExecuted
代表执行失败、准备执行、执行成功。quartz在JobRunShell类中,在线程执行前后触发调用加入监听器的方法。
example10 :Using Quartz Plug-Ins
插件的方式,是为了不对代码有太大的侵入。把JOB和trigger配置在xml文件上加载。好处是配置修改简单,不需要修改代码。
example11 :Quartz Under High Load
高负载,当线程池中的线程只有10个,而一次生成的定时任务是500个,不够用时执行任务是一个个执行。
而每个任务要延时0到2.5秒随机。11:58:40执行到11:59:46共用了60多秒。平均每个线程执行50个,每秒能执行不到1个。
当线程不够用时,执行的时间就是不准的。 所以需要监控线程池,线程不能满载超过30秒。
requestRecovery的作用在于当scheduler要down了,而任务正在执行时。可以通过scheduler重新执行。
example12 :Remote Job Scheduling using RMI
客户端注册JOB和trigger,由服务端执行。客户端和服务端通过RMI通信。
参考:http://www.blogjava.net/xiaohuzi2008/archive/2012/12/04/392408.html
example13: Clustered Quartz
Quartz集群,通过数据库集群。
example14: Trigger Priorities
优先级1-10,优先级越高则同一时间点先执行。
example15: TC Clustered Quartz
基于Terracotta的内存级别的集群。对于大量小任务时使用。
Cluster
Example:
JOB1执行SimpleRecoveryJob每5秒执行一次,共执行21次
JOB2 执行 SimpleRecoveryJob 每5秒执行一次,共执行21次
JOB3 执行 SimpleRecoveryStatefulJob 每3秒执行一次,共执行21次
JOB4执行SimpleRecoveryJob每4秒执行一次,共执行21次
JOB5执行SimpleRecoveryJob每4.5秒执行一次,共执行21次
SimpleRecove ryJob与 SimpleRecov eryStatefulJob都是延时执行10秒。
先instance2.bat设置启动参数为:
org.quartz.examples.example15.ClusterExample dontSche dule Jobs
就是instance2不加JOB,只由instance1加的JOB,然后instance1与instance2共同运行,以集群的方式运行。
每次循环只会在一个实例上运行。
图1:在instance1上打印
图2:在instance1上打印
图3:在instance2上打印
再看一下job3是 Sta tefulJob,必须在上一次执行完后才能触发。trigger3秒钟触发一次,但是job的执行需要10秒
所以效果就是10秒中执行一次。都是在实例1上执行。
如果instance2也有自己新增的job和trigger的话,instance1与instance2上新增的JOB在集群是共享的。
----
关于terracotta的使用、集群配置、持久化配置、quartz中的源码分析请访问:
http://blog.itpub.net/11627468/viewspace-1771336/
[INFO] 20 八月 09:26:09.842 上午 main [org.terracotta.quartz.DefaultClusteredJob
Store]
Synchronous write locking is [false]
- SimpleExample:主体程序
- HelloJob:一个简单的任务,打印Hello World
HelloJob:
点击(此处)折叠或打开
- public class HelloJob implements Job {
-
- private static Logger _log = LoggerFactory.getLogger(HelloJob.class);
-
-
- public HelloJob() {
- }
-
-
- public void execute(JobExecutionContext context)
- throws JobExecutionException {
-
- // Say Hello to the World and display the date/time
- _log.info("Hello World! - " + new Date());
- }
-
- }
SimpleExample:
点击(此处)折叠或打开
-
-
- package org.quartz.examples.example1;
-
- import static org.quartz.DateBuilder.evenMinuteDate;
- import static org.quartz.JobBuilder.newJob;
- import static org.quartz.TriggerBuilder.newTrigger;
-
- import org.quartz.JobDetail;
- import org.quartz.Scheduler;
- import org.quartz.SchedulerFactory;
- import org.quartz.Trigger;
- import org.quartz.impl.StdSchedulerFactory;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- import java.util.Date;
-
- /**
- * This Example will demonstrate how to start and shutdown the Quartz scheduler and how to schedule a job to run in
- * Quartz.
- *
- * @author Bill Kratzer
- */
- public class SimpleExample {
-
- public void run() throws Exception {
- Logger log = LoggerFactory.getLogger(SimpleExample.class);
-
- log.info("------- Initializing ----------------------");
-
- // First we must get a reference to a scheduler
- SchedulerFactory sf = new StdSchedulerFactory();
- Scheduler sched = sf.getScheduler();
-
- log.info("------- Initialization Complete -----------");
-
- // computer a time that is on the next round minute
- Date runTime = evenMinuteDate(new Date());
-
- log.info("------- Scheduling Job -------------------");
-
- // define the job and tie it to our HelloJob class
- JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build();
-
- // Trigger the job to run on the next round minute
- Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();
-
- // Tell quartz to schedule the job using our trigger
- sched.scheduleJob(job, trigger);
- log.info(job.getKey() + " will run at: " + runTime);
-
- // Start up the scheduler (nothing can actually run until the
- // scheduler has been started)
- sched.start();
-
- log.info("------- Started Scheduler -----------------");
-
- // wait long enough so that the scheduler as an opportunity to
- // run the job!
- log.info("------- Waiting 65 seconds... -------------");
- try {
- // wait 65 seconds to show job
- Thread.sleep(65L * 1000L);
- // executing...
- } catch (Exception e) {
- //
- }
-
- // shut down the scheduler
- log.info("------- Shutting Down ---------------------");
- sched.shutdown(true);
- log.info("------- Shutdown Complete -----------------");
- }
-
- public static void main(String[] args) throws Exception {
-
- SimpleExample example = new SimpleExample();
- example.run();
-
- }
-
- }
SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler();2)定义JobDetail和Trigger,并添加一个定时任务,在下一个整点执行:
Date runTime = evenMinuteDate(new Date()); // define the job and tie it to our HelloJob class Date runTime = evenMinuteDate(new Date());JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build(); Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build(); sched.scheduleJob(job, trigger);3)打开开关启动scheduler
// define the job and tie it to our HelloJob class sched.start();4)等待65秒后停止Scheduler
sched.shutdown(true);
Example 2 - Simple Triggers
1、实例内容也是Simple Trigger的使用,不一样的是批量的使用。
2、代码:
- SimpleTriggerExample:主体程序
- SimpleJob:一个简单的任务,打印当前时间
点击(此处)折叠或打开
- public void execute(JobExecutionContext context)
- throws JobExecutionException {
-
- // This job simply prints out its job name and the
- // date and time that it is running
- JobKey jobKey = context.getJobDetail().getKey();
- _log.info("SimpleJob says: " + jobKey + " executing at " + new Date());
- }
点击(此处)折叠或打开
- /*
- * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- */
-
- package org.quartz.examples.example2;
-
- import static org.quartz.DateBuilder.futureDate;
- import static org.quartz.JobBuilder.newJob;
- import static org.quartz.JobKey.jobKey;
- import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
- import static org.quartz.TriggerBuilder.newTrigger;
-
- import org.quartz.DateBuilder;
- import org.quartz.DateBuilder.IntervalUnit;
- import org.quartz.JobDetail;
- import org.quartz.Scheduler;
- import org.quartz.SchedulerFactory;
- import org.quartz.SchedulerMetaData;
- import org.quartz.SimpleTrigger;
- import org.quartz.impl.StdSchedulerFactory;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- import java.util.Date;
-
- /**
- * This Example will demonstrate all of the basics of scheduling capabilities of Quartz using Simple Triggers.
- *
- * @author Bill Kratzer
- */
- public class SimpleTriggerExample {
-
- public void run() throws Exception {
- Logger log = LoggerFactory.getLogger(SimpleTriggerExample.class);
-
- log.info("------- Initializing -------------------");
-
- // First we must get a reference to a scheduler
- SchedulerFactory sf = new StdSchedulerFactory();
- Scheduler sched = sf.getScheduler();
-
- log.info("------- Initialization Complete --------");
-
- log.info("------- Scheduling Jobs ----------------");
-
- // jobs can be scheduled before sched.start() has been called
-
- // get a "nice round" time a few seconds in the future...
- Date startTime = DateBuilder.nextGivenSecondDate(null, 15);
-
- // job1 will only fire once at date/time "ts"
- JobDetail job = newJob(SimpleJob.class).withIdentity("job1", "group1").build();
-
- SimpleTrigger trigger = (SimpleTrigger) newTrigger().withIdentity("trigger1", "group1").startAt(startTime).build();
-
- // schedule it to run!
- Date ft = sched.scheduleJob(job, trigger);
- log.info(job.getKey() + " will run at: " + ft + " and repeat: " + trigger.getRepeatCount() + " times, every "
- + trigger.getRepeatInterval() / 1000 + " seconds");
-
- // job2 will only fire once at date/time "ts"
- job = newJob(SimpleJob.class).withIdentity("job2", "group1").build();
-
- trigger = (SimpleTrigger) newTrigger().withIdentity("trigger2", "group1").startAt(startTime).build();
-
- ft = sched.scheduleJob(job, trigger);
- log.info(job.getKey() + " will run at: " + ft + " and repeat: " + trigger.getRepeatCount() + " times, every "
- + trigger.getRepeatInterval() / 1000 + " seconds");
-
- // job3 will run 11 times (run once and repeat 10 more times)
- // job3 will repeat every 10 seconds
- job = newJob(SimpleJob.class).withIdentity("job3", "group1").build();
-
- trigger = newTrigger().withIdentity("trigger3", "group1").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(10)).build();
-
- ft = sched.scheduleJob(job, trigger);
- log.info(job.getKey() + " will run at: " + ft + " and repeat: " + trigger.getRepeatCount() + " times, every "
- + trigger.getRepeatInterval() / 1000 + " seconds");
-
- // the same job (job3) will be scheduled by a another trigger
- // this time will only repeat twice at a 70 second interval
-
- trigger = newTrigger().withIdentity("trigger3", "group2").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(2)).forJob(job).build();
-
- ft = sched.scheduleJob(trigger);
- log.info(job.getKey() + " will [also] run at: " + ft + " and repeat: " + trigger.getRepeatCount()
- + " times, every " + trigger.getRepeatInterval() / 1000 + " seconds");
-
- // job4 will run 6 times (run once and repeat 5 more times)
- // job4 will repeat every 10 seconds
- job = newJob(SimpleJob.class).withIdentity("job4", "group1").build();
-
- trigger = newTrigger().withIdentity("trigger4", "group1").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(5)).build();
-
- ft = sched.scheduleJob(job, trigger);
- log.info(job.getKey() + " will run at: " + ft + " and repeat: " + trigger.getRepeatCount() + " times, every "
- + trigger.getRepeatInterval() / 1000 + " seconds");
-
- // job5 will run once, five minutes in the future
- job = newJob(SimpleJob.class).withIdentity("job5", "group1").build();
-
- trigger = (SimpleTrigger) newTrigger().withIdentity("trigger5", "group1")
- .startAt(futureDate(5, IntervalUnit.MINUTE)).build();
-
- ft = sched.scheduleJob(job, trigger);
- log.info(job.getKey() + " will run at: " + ft + " and repeat: " + trigger.getRepeatCount() + " times, every "
- + trigger.getRepeatInterval() / 1000 + " seconds");
-
- // job6 will run indefinitely, every 40 seconds
- job = newJob(SimpleJob.class).withIdentity("job6", "group1").build();
-
- trigger = newTrigger().withIdentity("trigger6", "group1").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInSeconds(40).repeatForever()).build();
-
- ft = sched.scheduleJob(job, trigger);
- log.info(job.getKey() + " will run at: " + ft + " and repeat: " + trigger.getRepeatCount() + " times, every "
- + trigger.getRepeatInterval() / 1000 + " seconds");
-
- log.info("------- Starting Scheduler ----------------");
-
- // All of the jobs have been added to the scheduler, but none of the jobs
- // will run until the scheduler has been started
- sched.start();
-
- log.info("------- Started Scheduler -----------------");
-
- // jobs can also be scheduled after start() has been called...
- // job7 will repeat 20 times, repeat every five minutes
- job = newJob(SimpleJob.class).withIdentity("job7", "group1").build();
-
- trigger = newTrigger().withIdentity("trigger7", "group1").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInMinutes(5).withRepeatCount(20)).build();
-
- ft = sched.scheduleJob(job, trigger);
- log.info(job.getKey() + " will run at: " + ft + " and repeat: " + trigger.getRepeatCount() + " times, every "
- + trigger.getRepeatInterval() / 1000 + " seconds");
-
- // jobs can be fired directly... (rather than waiting for a trigger)
- job = newJob(SimpleJob.class).withIdentity("job8", "group1").storeDurably().build();
-
- sched.addJob(job, true);
-
- log.info("'Manually' triggering job8...");
- sched.triggerJob(jobKey("job8", "group1"));
-
- log.info("------- Waiting 30 seconds... --------------");
-
- try {
- // wait 33 seconds to show jobs
- Thread.sleep(30L * 1000L);
- // executing...
- } catch (Exception e) {
- //
- }
-
- // jobs can be re-scheduled...
- // job 7 will run immediately and repeat 10 times for every second
- log.info("------- Rescheduling... --------------------");
- trigger = newTrigger().withIdentity("trigger7", "group1").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInMinutes(5).withRepeatCount(20)).build();
-
- ft = sched.rescheduleJob(trigger.getKey(), trigger);
- log.info("job7 rescheduled to run at: " + ft);
-
- log.info("------- Waiting five minutes... ------------");
- try {
- // wait five minutes to show jobs
- Thread.sleep(300L * 1000L);
- // executing...
- } catch (Exception e) {
- //
- }
-
- log.info("------- Shutting Down ---------------------");
-
- sched.shutdown(true);
-
- log.info("------- Shutdown Complete -----------------");
-
- // display some stats about the schedule that just ran
- SchedulerMetaData metaData = sched.getMetaData();
- log.info("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
-
- }
-
- public static void main(String[] args) throws Exception {
-
- SimpleTriggerExample example = new SimpleTriggerExample();
- example.run();
-
- }
-
- }
点击(此处)折叠或打开
- // job3 will run 11 times (run once and repeat 10 more times)
- // job3 will repeat every 10 seconds
- job = newJob(SimpleJob.class).withIdentity("job3", "group1").build();
-
- trigger = newTrigger().withIdentity("trigger3", "group1").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(10)).build();
因为在版本2.2.1中,是可以支持多个trigger来对应触发同一个JOB的。
点击(此处)折叠或打开
- trigger = newTrigger().withIdentity("trigger3", "group2").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(2)).forJob(job).build();
点击(此处)折叠或打开
- // job5 will run once, five minutes in the future
- job = newJob(SimpleJob.class).withIdentity("job5", "group1").build();
-
- trigger = (SimpleTrigger) newTrigger().withIdentity("trigger5", "group1")
- .startAt(futureDate(5, IntervalUnit.MINUTE)).build();
点击(此处)折叠或打开
- job = newJob(SimpleJob.class).withIdentity("job6", "group1").build();
-
- trigger = newTrigger().withIdentity("trigger6", "group1").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInSeconds(40).repeatForever()).build();
job8表示,不需要创建trigger,可以直接触发任务。
example3:Cron Trigger
job1是每20秒执行一次:
点击(此处)折叠或打开
- JobDetail job = newJob(SimpleJob.class)
- .withIdentity("job1", "group1")
- .build();
-
- CronTrigger trigger = newTrigger()
- .withIdentity("trigger1", "group1")
- .withSchedule(cronSchedule("0/20 * * * * ?"))
- .build();
-
- sched.scheduleJob(job, trigger);
点击(此处)折叠或打开
- JobDetail job = newJob(SimpleJob.class)
- .withIdentity("job1", "group1")
- .build();
-
- CronTrigger trigger = newTrigger()
- .withIdentity("trigger1", "group1")
- .withSchedule(cronSchedule("0/20 * * * * ?"))
- .build();
-
- sched.scheduleJob(job, trigger);
点击(此处)折叠或打开
- // job 3 will run every other minute but only between 8am and 5pm
- job = newJob(SimpleJob.class).withIdentity("job3", "group1").build();
-
- trigger = newTrigger().withIdentity("trigger3", "group1").withSchedule(cronSchedule("0 0/2 8-17 * * ?")).build();
-
- ft = sched.scheduleJob(job, trigger);
- log.info(job.getKey() + " has been scheduled to run at: " + ft + " and repeat based on expression: "
- + trigger.getCronExpression());
点击(此处)折叠或打开
- // job 5 will run at 10am on the 1st and 15th days of the month
- job = newJob(SimpleJob.class).withIdentity("job5", "group1").build();
-
- trigger = newTrigger().withIdentity("trigger5", "group1").withSchedule(cronSchedule("0 0 10am 1,15 * ?")).build();
-
- ft = sched.scheduleJob(job, trigger);
- log.info(job.getKey() + " has been scheduled to run at: " + ft + " and repeat based on expression: "
- + trigger.getCronExpression());
"0,30 * * ? * MON-FRI"
JOB7在每个周六和周日执行,每30秒执行一次。
"0,30 * * ? * MON-FRI"
Example 4 - Job Parameters and Job State
可以为job设置参数和状态。
@DisallowConcurrentExecution 注解的含义相关于1.8.6版本中的stateful job,就是说同一个trigger在上一次没有执行完成时,下一个不能执行。
比如trigger是30s执行一次,一个任务执行了37秒,那么第二个任务需要等37s后,前一个任务执行后了才可以执行。
那么第二个任务是在37s执行,还是在下一个周期60s的时候执行呢?
@PersistJobDataAfterExecution 代表执行完后的job的数据在jobMap中不共享,一般用于有状态的job...
点击(此处)折叠或打开
- @PersistJobDataAfterExecution
- @DisallowConcurrentExecution
- public class ColorJob implements Job {
看一下例子:
点击(此处)折叠或打开
- public class JobStateExample {
-
- public void run() throws Exception {
- Logger log = LoggerFactory.getLogger(JobStateExample.class);
-
- log.info("------- Initializing -------------------");
-
- // First we must get a reference to a scheduler
- SchedulerFactory sf = new StdSchedulerFactory();
- Scheduler sched = sf.getScheduler();
-
- log.info("------- Initialization Complete --------");
-
- log.info("------- Scheduling Jobs ----------------");
-
- // get a "nice round" time a few seconds in the future....
- Date startTime = nextGivenSecondDate(null, 10);
-
- // job1 will only run 5 times (at start time, plus 4 repeats), every 10 seconds
- JobDetail job1 = newJob(ColorJob.class).withIdentity("job1", "group1").build();
-
- SimpleTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(4)).build();
-
- // pass initialization parameters into the job
- job1.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Green");
- job1.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
-
- // schedule the job to run
- Date scheduleTime1 = sched.scheduleJob(job1, trigger1);
- log.info(job1.getKey() + " will run at: " + scheduleTime1 + " and repeat: " + trigger1.getRepeatCount()
- + " times, every " + trigger1.getRepeatInterval() / 1000 + " seconds");
-
- // job2 will also run 5 times, every 10 seconds
- JobDetail job2 = newJob(ColorJob.class).withIdentity("job2", "group1").build();
-
- SimpleTrigger trigger2 = newTrigger().withIdentity("trigger2", "group1").startAt(startTime)
- .withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(4)).build();
-
- // pass initialization parameters into the job
- // this job has a different favorite color!
- job2.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Red");
- job2.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
-
- // schedule the job to run
- Date scheduleTime2 = sched.scheduleJob(job2, trigger2);
- log.info(job2.getKey().toString() + " will run at: " + scheduleTime2 + " and repeat: " + trigger2.getRepeatCount()
- + " times, every " + trigger2.getRepeatInterval() / 1000 + " seconds");
-
- log.info("------- Starting Scheduler ----------------");
-
- // All of the jobs have been added to the scheduler, but none of the jobs
- // will run until the scheduler has been started
- sched.start();
-
- log.info("------- Started Scheduler -----------------");
-
- log.info("------- Waiting 60 seconds... -------------");
- try {
- // wait five minutes to show jobs
- Thread.sleep(60L * 1000L);
- // executing...
- } catch (Exception e) {
- //
- }
-
- log.info("------- Shutting Down ---------------------");
-
- sched.shutdown(true);
-
- log.info("------- Shutdown Complete -----------------");
-
- SchedulerMetaData metaData = sched.getMetaData();
- log.info("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
-
- }
job1是red,job2是green ,每个job每10秒执行一次,那么假如一个任务需要执行3 5 秒呢?下一次执行在什么时候?
答: 按照 trigger 的规则,第 20 秒和第 30 秒分别会有一次预定的触发执行,但是由于是有状态任务,因此实际不会被触发。在第 35秒第一次任务执行完毕时,Quartz 会把第 20秒 和第 30秒的两次触发作为 misfired job 进行处理。对于 misfired job,Quartz 会查看其 misfire 策略是如何设定的,如果是立刻执行,则会马上启动一次执行,如果是等待下次执行,则会忽略错过的任务,而等待下次(即第 6 分钟)触发执行。
有状态的job如果第一次没有执行完,第二次执行的时间错过了,就会被认为是misfire
doUpdateOfMisfiredTrigger调用CronTriggerImpl的updateAfterMisfire方法。
如果misfire设置为MISFIRE_INSTRUCTION_SMART_POLICY或MISFIRE_INSTRUCTION_FIRE_ONCE_NOW就是马上执行。
如果misfire设置为MISFIRE_INSTRUCTION_DO_NOTHING则在下一个周期再执行。
默认是:MISFIRE_INSTRUCTION_SMART_POLICY
可以通过trigger的build方法增加参数实现:
withMisfireHandlingInstructionFireAndProceed().build()
withMisfireHandlingInstructionDoNothing().build()
example5: Job Misfire
Class Name | Description |
---|---|
MisfireExample | 主程序 |
StatefulDumbJob | 每10秒执行一次 |
JOB1:每3秒执行一次,执行的任务是延时10秒的。statefulJob是有状态的,上一个任务没执行完,是不会执行的。
点击(此处)折叠或打开
- JobDetail job = newJob(StatefulDumbJob.class)
- .withIdentity("statefulJob1", "group1")
- .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L)
- .build();
-
- SimpleTrigger trigger = newTrigger()
- .withIdentity("trigger1", "group1")
- .startAt(startTime)
- .withSchedule(simpleSchedule()
- .withIntervalInSeconds(3)
- .repeatForever())
- .build();
-
- sched.scheduleJob(job, trigger);
点击(此处)折叠或打开
- job = newJob(StatefulDumbJob.class)
- .withIdentity("statefulJob2", "group1")
- .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L)
- .build();
-
- trigger = newTrigger()
- .withIdentity("trigger2", "group1")
- .startAt(startTime)
- .withSchedule(simpleSchedule()
- .withIntervalInSeconds(3)
- .repeatForever()
- .withMisfireHandlingInstructionNowWithExistingCount()) // set misfire instruction
- .build();
点击(此处)折叠或打开
- if (instr == Trigger.MISFIRE_INSTRUCTION_SMART_POLICY) {
- if (getRepeatCount() == 0) {
- instr = MISFIRE_INSTRUCTION_FIRE_NOW;
- } else if (getRepeatCount() == REPEAT_INDEFINITELY) {
- instr = MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT;
- } else {
- // if (getRepeatCount() > 0)
- instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT;
- }
- }
点击(此处)折叠或打开
- if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) {
- instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
- }
example6 : Dealing with Job Exceptions
在JOB1中如果出现异常了,可以通过抛出异常的方式来重新执行一次。
点击(此处)折叠或打开
- try {
- int zero = 0;
- int calculation = 4815 / zero;
- }
- catch (Exception e) {
- _log.info("--- Error in job!");
- JobExecutionException e2 =
- new JobExecutionException(e);
- // this job will refire immediately
- e2.refireImmediately();
- throw e2;
- }
点击(此处)折叠或打开
- try {
- int zero = 0;
- int calculation = 4815 / zero;
- }
- catch (Exception e) {
- _log.info("--- Error in job!");
- JobExecutionException e2 =
- new JobExecutionException(e);
- // Quartz will automatically unschedule
- // all triggers associated with this job
- // so that it does not run again
- e2.setUnscheduleAllTriggers(true);
- throw e2;
- }
example7 :Interrupting Jobs
Class Name | Description |
---|---|
InterruptExample | 主程序 |
DumbInterruptableJob | 可中断JOB,4秒时间,for循环可以被中断 |
点击(此处)折叠或打开
- public void interrupt() throws UnableToInterruptJobException {
- _log.info("---" + _jobKey + " -- INTERRUPTING --");
- _interrupted = true;
- }
点击(此处)折叠或打开
- for (int i = 0; i < 4; i++) {
- try {
- Thread.sleep(1000L);
- } catch (Exception ignore) {
- ignore.printStackTrace();
- }
-
- // periodically check if we've been interrupted...
- if(_interrupted) {
- _log.info("--- " + _jobKey + " -- Interrupted... bailing out!");
- return; // could also choose to throw a JobExecutionException
- // if that made for sense based on the particular
- // job's responsibilities/behaviors
- }
- }
由代码sched.interrupt()看出来中断只针对当前sched实例,不是针对集群的。如果是集群,需要每个实例都触发。
点击(此处)折叠或打开
- for (int i = 0; i < 50; i++) {
- try {
- Thread.sleep(7000L);
- // tell the scheduler to interrupt our job
- sched.interrupt(job.getKey());
- } catch (Exception e) {
- //
- }
- }
点击(此处)折叠或打开
- for (int i = 0; i < 50; i++) {
- try {
- Thread.sleep(7000L);
- // tell the scheduler to interrupt our job
- sched.interrupt(job.getKey());
- } catch (Exception e) {
- //
- }
- }
example8 :Fun with Calendars
可以设置一年内的某几天不执行。
点击(此处)折叠或打开
- // Add the holiday calendar to the schedule
- AnnualCalendar holidays = new AnnualCalendar();
-
- // fourth of July (July 4)
- Calendar fourthOfJuly = new GregorianCalendar(2005, 6, 4);
- holidays.setDayExcluded(fourthOfJuly, true);
- // halloween (Oct 31)
- Calendar halloween = new GregorianCalendar(2005, 9, 31);
- holidays.setDayExcluded(halloween, true);
- // christmas (Dec 25)
- Calendar christmas = new GregorianCalendar(2005, 11, 25);
- holidays.setDayExcluded(christmas, true);
-
- // tell the schedule about our holiday calendar
- sched.addCalendar("holidays", holidays, false, false);
-
- // schedule a job to run hourly, starting on halloween
- // at 10 am
- Date runDate = dateOf(0, 0, 10, 31, 10);
-
- JobDetail job = newJob(SimpleJob.class).withIdentity("job1", "group1").build();
-
- SimpleTrigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runDate)
- .withSchedule(simpleSchedule().withIntervalInHours(1).repeatForever()).modifiedByCalendar("holidays").build();
-
- // schedule the job and print the first run date
- Date firstRunTime = sched.scheduleJob(job, trigger);
通过监听器可以用于日志、报表的处理。
也可以通过JOB监听器实现在JOB执行前后调用其它JOB,实现简单的任务流程。
监听器需要实现接口JobListener,可以实现jobExecutionVetoed,jobToBeExecuted,jobWasExecuted
代表执行失败、准备执行、执行成功。quartz在JobRunShell类中,在线程执行前后触发调用加入监听器的方法。
点击(此处)折叠或打开
- public class Job1Listener implements JobListener {
-
- private static Logger _log = LoggerFactory.getLogger(Job1Listener.class);
-
- public String getName() {
- return "job1_to_job2";
- }
-
- public void jobToBeExecuted(JobExecutionContext inContext) {
- _log.info("Job1Listener says: Job Is about to be executed.");
- }
-
- public void jobExecutionVetoed(JobExecutionContext inContext) {
- _log.info("Job1Listener says: Job Execution was vetoed.");
- }
-
- public void jobWasExecuted(JobExecutionContext inContext, JobExecutionException inException) {
- _log.info("Job1Listener says: Job was executed.");
-
- // Simple job #2
- JobDetail job2 = newJob(SimpleJob2.class).withIdentity("job2").build();
-
- Trigger trigger = newTrigger().withIdentity("job2Trigger").startNow().build();
-
- try {
- // schedule the job to run!
- inContext.getScheduler().scheduleJob(job2, trigger);
- } catch (SchedulerException e) {
- _log.warn("Unable to schedule job2!");
- e.printStackTrace();
- }
-
- }
-
- }
example10 :Using Quartz Plug-Ins
插件的方式,是为了不对代码有太大的侵入。把JOB和trigger配置在xml文件上加载。好处是配置修改简单,不需要修改代码。
点击(此处)折叠或打开
- <job>
- <name>TestJob2</name>
- <group>GroupOfTestJob2</group>
- <description>This is the description of TestJob2</description>
- <job-class>org.quartz.examples.example10.SimpleJob</job-class>
- <durability>false</durability>
- <recover>true</recover>
- <job-data-map>
- <entry>
- <key>someKey</key>
- <value>someValue</value>
- </entry>
- <entry>
- <key>someOtherKey</key>
- <value>someOtherValue</value>
- </entry>
- </job-data-map>
- </job>
-
- <trigger>
- <simple>
- <name>TestSimpleTrigger2AtTenSecondIntervalAndFiveRepeats</name>
- <group>GroupOfTestJob2Triggers</group>
- <job-name>TestJob2</job-name>
- <job-group>GroupOfTestJob2</job-group>
- <start-time>2010-02-09T10:15:00</start-time>
- <misfire-instruction>MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT</misfire-instruction>
- <repeat-count>5</repeat-count>
- <repeat-interval>10000</repeat-interval>
- </simple>
- </trigger>
example11 :Quartz Under High Load
高负载,当线程池中的线程只有10个,而一次生成的定时任务是500个,不够用时执行任务是一个个执行。
而每个任务要延时0到2.5秒随机。11:58:40执行到11:59:46共用了60多秒。平均每个线程执行50个,每秒能执行不到1个。
当线程不够用时,执行的时间就是不准的。 所以需要监控线程池,线程不能满载超过30秒。
点击(此处)折叠或打开
- // schedule 500 jobs to run
- for (int count = 1; count <= _numberOfJobs; count++) {
- JobDetail job = newJob(SimpleJob.class).withIdentity("job" + count, "group_1").requestRecovery() // ask scheduler
- // to re-execute
- // this job if it
- // was in
- // progress when
- // the scheduler
- // went down...
- .build();
-
- // tell the job to delay some small amount... to simulate work...
- long timeDelay = (long) (java.lang.Math.random() * 2500);
- job.getJobDataMap().put(SimpleJob.DELAY_TIME, timeDelay);
-
- Trigger trigger = newTrigger().withIdentity("trigger_" + count, "group_1")
- .startAt(futureDate((10000 + (count * 100)), IntervalUnit.MILLISECOND)) // space fire times a small bit
- .build();
-
- sched.scheduleJob(job, trigger);
- if (count % 25 == 0) {
- log.info("...scheduled " + count + " jobs");
- }
- }
requestRecovery的作用在于当scheduler要down了,而任务正在执行时。可以通过scheduler重新执行。
example12 :Remote Job Scheduling using RMI
客户端注册JOB和trigger,由服务端执行。客户端和服务端通过RMI通信。
参考:http://www.blogjava.net/xiaohuzi2008/archive/2012/12/04/392408.html
example13: Clustered Quartz
Quartz集群,通过数据库集群。
example14: Trigger Priorities
优先级1-10,优先级越高则同一时间点先执行。
example15: TC Clustered Quartz
基于Terracotta的内存级别的集群。对于大量小任务时使用。
Class Name | Description |
---|---|
ClusterExample | 主程序 |
SimpleRecoveryJob SimpleRecoveryStatefulJob | |
JOB1执行SimpleRecoveryJob每5秒执行一次,共执行21次
JOB2 执行 SimpleRecoveryJob 每5秒执行一次,共执行21次
JOB3 执行 SimpleRecoveryStatefulJob 每3秒执行一次,共执行21次
JOB4执行SimpleRecoveryJob每4秒执行一次,共执行21次
JOB5执行SimpleRecoveryJob每4.5秒执行一次,共执行21次
SimpleRecove ryJob与 SimpleRecov eryStatefulJob都是延时执行10秒。
先instance2.bat设置启动参数为:
org.quartz.examples.example15.ClusterExample dontSche dule Jobs
就是instance2不加JOB,只由instance1加的JOB,然后instance1与instance2共同运行,以集群的方式运行。
每次循环只会在一个实例上运行。
图1:在instance1上打印
图2:在instance1上打印
图3:在instance2上打印
再看一下job3是 Sta tefulJob,必须在上一次执行完后才能触发。trigger3秒钟触发一次,但是job的执行需要10秒
所以效果就是10秒中执行一次。都是在实例1上执行。
如果instance2也有自己新增的job和trigger的话,instance1与instance2上新增的JOB在集群是共享的。
----
关于terracotta的使用、集群配置、持久化配置、quartz中的源码分析请访问:
http://blog.itpub.net/11627468/viewspace-1771336/
[INFO] 20 八月 09:26:09.842 上午 main [org.terracotta.quartz.DefaultClusteredJob
Store]
Synchronous write locking is [false]