Quartz入门
一、简介
Quartz是一个功能丰富的开源作业调度库,可以用于创建简单或复杂的计划来执行数十数百甚至数万个作业。
二、安装与启动
1、安装
下载并解压,压缩文件中包括文档、样例和Jar等,需要将lib下的jar添加到项目中。
或者使用Maven依赖:
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
如果需要使用日志,还要引入:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
2、启动
启动并运行一个调度程序:
public class QuartzTest {
public static void main(String[] args) {
try {
// Grab the Scheduler instance from the Factory
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// and start it off
scheduler.start();
// do something
scheduler.shutdown();
} catch (SchedulerException se) {
se.printStackTrace();
}
}
}
三、配置
Quartz的配置文件使用一个名为quartz.properties
的属性文件,此文件可以不设置,默认使用quartz.jar
中的org/quartz/quartz.properties
配置文件。
例如,修改默认配置:
# 将调度(计划)程序的名字改为MyScheduler
org.quartz.scheduler.instanceName = MyScheduler
# 设置线程中的线程数为3
org.quartz.threadPool.threadCount = 3
# Quartz的数据(作业和触发器的详细信息等)保存到内存中(而不是数据库中)
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
四、样例
1、HelloWorld
- Job
Job类需实现org.quartz.Job
接口,重写execute()
方法:
public class HelloJob implements Job{
private static Logger log = LoggerFactory.getLogger(HelloJob.class);
/**
* Empty constructor for job initilization
* Quartz requires a public empty constructor so that the
* scheduler can instantiate the class whenever it needs.
*/
public HelloJob() {
}
/**
* Called by the org.quartz.Scheduler when a org.quartz.Trigger fires that is associated with the Job
*
* @throws JobExecutionException
* if there is an exception while executing the job.
*/
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("Hello World! - " + new Date());
}
}
- Application
public class SimpleExample {
public static void main(String[] args) 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 = new Date();
log.info("------- Scheduling Job -------------------");
// define the job and tie it to our HelloJob class
JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();
// Trigger the job to run on the next round minute
Trigger trigger = TriggerBuilder.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 3 seconds... -------------");
try {
// wait 3 seconds to show job
Thread.sleep(3000);
// executing...
} catch (Exception e) {
//
}
// shut down the scheduler
log.info("------- Shutting Down ---------------------");
sched.shutdown(true);
log.info("------- Shutdown Complete -----------------");
}
}
log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="default" class="org.apache.log4j.ConsoleAppender">
<param name="target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%p] %d{dd MMM hh:mm:ss.SSS aa} %t [%c]%n%m%n%n"/>
</layout>
</appender>
<logger name="org.quartz">
<level value="info" />
</logger>
<root>
<level value="info" />
<appender-ref ref="default" />
</root>
</log4j:configuration>
2、简单触发器
本示例(SimpleTriggerExample)演示使用简单触发器的Quartz调度功能。
- SimpleJob.java
public class SimpleJob implements Job{
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
System.out.printf("SimpleJob says: %s executing at %s. \n", jobKey, new Date());
}
}
- SimpleTriggerExample
Job1只执行一次,Job2每3秒执行一次,重复两次:
public class SimpleTriggerExample {
public static void main(String[] args) throws Exception {
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
Date startTime = new Date();
//job1
JobDetail job = JobBuilder.newJob(SimpleJob.class).withIdentity("job1", "group1").build();
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startAt(startTime).build();
Date ft = scheduler.scheduleJob(job, trigger);
System.out.printf("%s will run at: %s and repeat: %s times, every %s seconds.\n", job.getKey(), ft, trigger.getRepeatCount(), trigger.getRepeatInterval() / 1000);
//job2
job = JobBuilder.newJob(SimpleJob.class).withIdentity("job2", "group1").build();
trigger = TriggerBuilder.newTrigger().withIdentity("trigger2", "group1").startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).withRepeatCount(2))
.build();
ft = scheduler.scheduleJob(job, trigger);
System.out.printf("%s will run at: %s and repeat: %s times, every %s seconds.\n", job.getKey(), ft, trigger.getRepeatCount(), trigger.getRepeatInterval() / 1000);
scheduler.start();
try {
// wait 10 seconds to show jobs
Thread.sleep(10000L);
// executing...
} catch (Exception e) {
//
}
scheduler.shutdown(true);
}
}
- 输出
group1.job1 will run at: Sat Feb 13 00:09:08 CST 2021 and repeat: 0 times, every 0 seconds.
group1.job2 will run at: Sat Feb 13 00:09:08 CST 2021 and repeat: 2 times, every 3 seconds.
SimpleJob says: group1.job1 executing at Sat Feb 13 00:09:08 CST 2021.
SimpleJob says: group1.job2 executing at Sat Feb 13 00:09:08 CST 2021.
SimpleJob says: group1.job2 executing at Sat Feb 13 00:09:11 CST 2021.
SimpleJob says: group1.job2 executing at Sat Feb 13 00:09:14 CST 2021.
3、Cron触发器
本示例(CronTriggerExample)演示使用Cron触发器的Quartz调度功能。Cron表达式的介绍参考Cron表达式简介。
- SimpleJob.java
public class SimpleJob implements Job{
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
System.out.printf("SimpleJob says: %s executing at %s. \n", jobKey, new Date());
}
}
- CronTriggerExample
Job1每10秒执行一次,Job2在每月12号和13号凌晨0:33执行一次:
public class CronTriggerExample {
public static void main(String[] args) throws Exception {
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
// job1
JobDetail job = JobBuilder.newJob(SimpleJob.class).withIdentity("job1", "group1").build();
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).build();
Date ft = scheduler.scheduleJob(job, trigger);
System.out.printf("%s has been scheduled to run at: %s and repeat based on expression: %s.\n", job.getKey(), ft,
trigger.getCronExpression());
// job2
job = JobBuilder.newJob(SimpleJob.class).withIdentity("job2", "group1").build();
trigger = TriggerBuilder.newTrigger().withIdentity("trigger2", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 33 0 12,13 * ?")).build();
ft = scheduler.scheduleJob(job, trigger);
System.out.printf("%s has been scheduled to run at: %s and repeat based on expression: %s.\n", job.getKey(), ft,
trigger.getCronExpression());
scheduler.start();
try {
// wait 30 seconds to show jobs
Thread.sleep(30 * 1000);
// executing...
} catch (Exception e) {
//
}
scheduler.shutdown(true);
SchedulerMetaData metaData = scheduler.getMetaData();
System.out.printf("Executed %s jobs.", metaData.getNumberOfJobsExecuted());
}
}
- 输出
group1.job1 has been scheduled to run at: Sat Feb 13 00:33:00 CST 2021 and repeat based on expression: 0/10 * * * * ?.
group1.job2 has been scheduled to run at: Sat Feb 13 00:33:00 CST 2021 and repeat based on expression: 0 33 0 12,13 * ?.
SimpleJob says: group1.job1 executing at Sat Feb 13 00:33:00 CST 2021.
SimpleJob says: group1.job2 executing at Sat Feb 13 00:33:00 CST 2021.
SimpleJob says: group1.job1 executing at Sat Feb 13 00:33:10 CST 2021.
SimpleJob says: group1.job1 executing at Sat Feb 13 00:33:20 CST 2021.
SimpleJob says: group1.job1 executing at Sat Feb 13 00:33:30 CST 2021.
Executed 5 jobs.
4、Job参数和状态
本示例(Job Parameters and Job State)演示如何将运行时参数传递给Quartz作业(Job),以及如何维护作业中的状态。
- Job
ColorJob除了实现Job接口外还需增加@PersistJobDataAfterExecution
和@DisallowConcurrentExecution
注解:
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class ColorJob implements Job{
public static final String FAVORITE_COLOR = "favorite color";
public static final String EXECUTION_COUNT = "count";
private int _counter = 1;
public ColorJob() {
// TODO Auto-generated constructor stub
}
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
JobDataMap data = context.getJobDetail().getJobDataMap();
String favoriteColor = data.getString(FAVORITE_COLOR);
int count = data.getInt(EXECUTION_COUNT);
StringBuilder info = new StringBuilder();
info.append("ColorJob: %s executing at %s \n");
info.append(" favorite color is %s \n");
info.append(" execution count (from job map) is %s \n");
info.append(" execution count (from job member variable) is %s \n\n");
System.out.printf(info.toString(), jobKey, new Date(), favoriteColor, count, _counter);
count++;
data.put(EXECUTION_COUNT, count);
_counter++;
}
}
- Application
Job1每2秒执行一次,重复3次;Job2每1秒执行一次,重复2次:
public class JobStateExample {
public static void main(String[] args) throws Exception {
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
System.out.printf("current time: %s\n", new Date());
// get a "nice round" time a few seconds in the future....
Date startTime = DateBuilder.nextGivenSecondDate(null, 10);
// job1
JobDetail job = JobBuilder.newJob(ColorJob.class).withIdentity("job1", "group1").build();
SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).withRepeatCount(3))
.build();
job.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Green");
job.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
Date scheduleTime = scheduler.scheduleJob(job, trigger);
System.out.printf("%s will run at: %s and repeat: %s times, every %s seconds\n", job.getKey(), scheduleTime,
trigger.getRepeatCount(), trigger.getRepeatInterval() / 1000);
// job2
job = JobBuilder.newJob(ColorJob.class).withIdentity("job2", "group1").build();
trigger = TriggerBuilder.newTrigger().withIdentity("trigger2", "group1").startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).withRepeatCount(2))
.build();
job.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Red");
job.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
scheduleTime = scheduler.scheduleJob(job, trigger);
System.out.printf("%s will run at: %s and repeat: %s times, every %s seconds\n\n", job.getKey(), scheduleTime,
trigger.getRepeatCount(), trigger.getRepeatInterval() / 1000);
scheduler.start();
try {
// wait five minutes to show jobs
Thread.sleep(15 * 1000);
// executing...
} catch (Exception e) {
//
}
scheduler.shutdown(true);
SchedulerMetaData metaData = scheduler.getMetaData();
System.out.printf("Executed %s jobs.", metaData.getNumberOfJobsExecuted());
}
}
- 输出
current time: Sat Feb 13 13:17:24 CST 2021
group1.job1 will run at: Sat Feb 13 13:17:30 CST 2021 and repeat: 3 times, every 2 seconds
group1.job2 will run at: Sat Feb 13 13:17:30 CST 2021 and repeat: 2 times, every 1 seconds
ColorJob: group1.job1 executing at Sat Feb 13 13:17:30 CST 2021
favorite color is Green
execution count (from job map) is 1
execution count (from job member variable) is 1
ColorJob: group1.job2 executing at Sat Feb 13 13:17:30 CST 2021
favorite color is Red
execution count (from job map) is 1
execution count (from job member variable) is 1
ColorJob: group1.job2 executing at Sat Feb 13 13:17:31 CST 2021
favorite color is Red
execution count (from job map) is 2
execution count (from job member variable) is 1
ColorJob: group1.job1 executing at Sat Feb 13 13:17:32 CST 2021
favorite color is Green
execution count (from job map) is 2
execution count (from job member variable) is 1
ColorJob: group1.job2 executing at Sat Feb 13 13:17:32 CST 2021
favorite color is Red
execution count (from job map) is 3
execution count (from job member variable) is 1
ColorJob: group1.job1 executing at Sat Feb 13 13:17:34 CST 2021
favorite color is Green
execution count (from job map) is 3
execution count (from job member variable) is 1
ColorJob: group1.job1 executing at Sat Feb 13 13:17:36 CST 2021
favorite color is Green
execution count (from job map) is 4
execution count (from job member variable) is 1
Executed 7 jobs.
5、异常处理
本示例(Dealing with Job Exceptions)演示如何处理作业执行异常;Quartz中的作业允许抛出JobExecutionExceptions
异常,当抛出此异常时可以指定Quartz采取什么操作。
- BadJobOne.java
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class BadJobOne implements Job{
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
int denominator = dataMap.getInt("denominator");
System.out.printf("---%s executing at %s with denominator %s\n", jobKey, new Date(), denominator);
try {
int calculation = 6666 / denominator;
}catch (Exception e) {
System.out.printf("---Error in job %s\n", jobKey);
JobExecutionException je = new JobExecutionException(e);
//修复分母,下次运行此作业时不会再失败
dataMap.put("denominator", 1);
//立即重新激活作业
je.setRefireImmediately(true);
throw je;
}
System.out.printf("---%s completed at %s\n", jobKey, new Date());
}
}
- BadJobTwo
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class BadJobTwo implements Job{
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
System.out.printf("***%s executing at %s\n", jobKey, new Date());
try {
int zero = 0;
int calculation = 5555 / zero;
}catch(Exception e) {
System.out.printf("***Error in job %s\n", jobKey);
JobExecutionException je = new JobExecutionException(e);
//Quartz将自动取消与该作业相关的所有触发器,因此它不会再运行
je.setUnscheduleAllTriggers(true);
throw je;
}
System.out.printf("***%s completed at %s\n", jobKey, new Date());
}
}
- JobExceptionExample.java
job1每5秒运行一次,此job将会抛出异常并修复错误后立即重新激活执行;job2每3秒运行一次,此job在抛出异常后不会再激活执行。
public class JobExceptionExample {
public static void main(String[] args) throws Exception {
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
System.out.printf("current time: %s\n", new Date());
Date startTime = DateBuilder.nextGivenSecondDate(null, 10);
// job1
JobDetail job = JobBuilder.newJob(BadJobOne.class).withIdentity("badJobOne", "group1")
.usingJobData("denominator", 0).build();
SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
Date scheduleTime = scheduler.scheduleJob(job, trigger);
System.out.printf("%s will run at: %s and repeat: %s times, every %s seconds\n", job.getKey(), scheduleTime,
trigger.getRepeatCount(), trigger.getRepeatInterval() / 1000);
// job2
job = JobBuilder.newJob(BadJobTwo.class).withIdentity("badJobTwo", "group1").build();
trigger = TriggerBuilder.newTrigger().withIdentity("trigger2", "group2").startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever()).build();
scheduleTime = scheduler.scheduleJob(job, trigger);
System.out.printf("%s will run at: %s and repeat: %s times, every %s seconds\n", job.getKey(), scheduleTime,
trigger.getRepeatCount(), trigger.getRepeatInterval() / 1000);
scheduler.start();
try {
// sleep for 15 seconds
Thread.sleep(15 * 1000);
} catch (Exception e) {
//
}
scheduler.shutdown(false);
SchedulerMetaData metaData = scheduler.getMetaData();
System.out.printf("Executed %s jobs.", metaData.getNumberOfJobsExecuted());
}
}
- 输出
current time: Sat Feb 13 14:05:37 CST 2021
group1.badJobOne will run at: Sat Feb 13 14:05:40 CST 2021 and repeat: -1 times, every 5 seconds
group1.badJobTwo will run at: Sat Feb 13 14:05:40 CST 2021 and repeat: -1 times, every 3 seconds
---group1.badJobOne executing at Sat Feb 13 14:05:40 CST 2021 with denominator 0
---Error in job group1.badJobOne
***group1.badJobTwo executing at Sat Feb 13 14:05:40 CST 2021
---group1.badJobOne executing at Sat Feb 13 14:05:40 CST 2021 with denominator 1
---group1.badJobOne completed at Sat Feb 13 14:05:40 CST 2021
***Error in job group1.badJobTwo
---group1.badJobOne executing at Sat Feb 13 14:05:45 CST 2021 with denominator 1
---group1.badJobOne completed at Sat Feb 13 14:05:45 CST 2021
---group1.badJobOne executing at Sat Feb 13 14:05:50 CST 2021 with denominator 1
---group1.badJobOne completed at Sat Feb 13 14:05:50 CST 2021
Executed 5 jobs.