Quartz以模块方式架构,因此,要使它运行,几个组件必须很好的咬合在一起,幸运的是,已经有了一些现存的助手可以完成这些工作,所有的Scheduler实例由SchedulerFactory创建。
Quartz的三个核心概念:调度器、任务、触发器,三者之间的关系
SchedulerFactory ——> Scheduler ——> Trigger/job
SchedulerFactory 接口,有两个子类,一个是StdSchedulerFactory,另一个是DirectSchedulerFactory,我们之前用的都是StdSchedulerFactory。
一个作业,比较重要的三要素是Scheduler、JobDetail、Trigger,而Trigger对于Job而言就好比一个驱动器,没有触发器来定时驱动作业,作业就无法运行,对于Job而言,一个Job可以对应多个Trigger,单对于Trigger而言,一个Trigger只能对应一个Job,所以一个Trigger只能被指派给一个job,所以如果我们需要一个复杂的触发计划,可以创建多个Trigger并指派他们给同一个job。
Quartz默认的SchedulerFactory。
1. 使用一组参数(java.util.Properties)来创建和初始化Quartz调度器。
2. 配置参数一般存储在quartz.properties文件中。
3. 调用getScheduler方法就能创建和初始化调度器对象。例如:
// Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
SchedulerFactory s = new StdSchedulerFactory();
Scheduler scheduler = s.getScheduler();
注意:看源码知道,StdSchedulerFactory.getDefaultScheduler();内部实现就是用的该方式。
1. 获取调度器开始时间
Date startDate = scheduler.scheduleJob(jobDetail, trigger);
2. 启动任务调度
scheduler.start();
3. 任务调度挂起、暂停
scheduler.standby();
4. 关闭任务调度
scheduler.shutdown();
scheduler.shutdown(true):表示等待所有正在执行的job执行完毕之后,再关闭Scheduler
scheduler.shutdown(false):表示直接关闭Scheduler
DirectSchedulerFactory是对SchedulerFactory的直接实现,通过它可以直接构建Scheduler、Threadpool等。
DirectSchedulerFactory instance = DirectSchedulerFactory.getInstance();
Scheduler scheduler1 = instance.getScheduler();
默认路径:quartz-2.3.0中的org.quartz.properties
注意:如果不想用默认的配置文件,我们也可以在资源目录下新建quartz.properties配置文件,在该文件下写自己的配置信息
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
1. org.quartz.scheduler.instanceName:用来区分特定的调度器实例,可以按照功能用途给调度器起名字。
2. org.quartz.scheduler.instanceId:也是用来区分调度器实例的,允许是任何的字符串,但是这个值必须在所有的调度器实例中是唯一的,尤其在一个集群环境中,作为集群的唯一key。
注意:如果想Quartz自动生成该值,可以将其设置成AUTO。
例如: org.quartz.scheduler.instanceId:AUTO
1. threadCount:处理job的线程个数,至少为1,但是最多的话最好不要超过100个,在多数机器上超过100个就显得相当不实用,特别是在job执行时间较长的情况下。
2. threadPriority:线程的优先级, 优先级高的线程比优先级的线程先得到执行。最小为1,最大为10,默认为5
3. threadPool.class:一个实现了org.quartz.ThreadPool接口的类,Quartz自带的线程池实现类是org.quartz.smpl.SimpleThreadPool
描述了在调度器实例的生命周期中,Job和Trigger信息是如何被存储的
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
满足特定需求用到的Quartz插件的配置
Quartz的监听器用于当任务调度中,你所关注事件发生时,能够及时获取这一事件通知。类似于任务执行过程中的邮件、短信提醒。
Quartz监听器主要有JobListener、TriggerListener、SchedulerListener三种,分别表示任务、触发器、调度器对应的监听器三者使用方式类似。
全局监听器能接收到所有的Job/Trigger 的事件通知
// 创建一个全局的监听器
scheduler.getListenerManager().addJobListener(new MyJobListener(),EverythingMatcher.allJobs());
非全局的监听器只能接收到在其上注册的job或trigger的事件,不在其注册的job或trigger则 不会进行监听。
// 创建一个局部监听器
scheduler.getListenerManager().addJobListener(new MyJobListener(),KeyMatcher.keyEquals(JobKey.jobKey("job1", "group1")));
任务调度过程中,与任务Job相关的事件包括:job开始要执行的提示,job执行完成的提示灯
1. getName():用于获取JobListener的名称
2. jobToBeExecuted(JobExecutionContext context):Scheduler在jobDetail将要被执行时调用这个方法
3. jobExecutionVetoed(JobExecutionContext context):Scheduler在jobDetail即将被执行,但又被TriggerListener否决时会调用该方法。
4. jobWasExecuted():Scheduler在JobDetail被执行之后调用该方法。
1. 创建监听器类
package cn.bjc.quartz.listener;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
public class MyJobListener implements JobListener{
@Override
public String getName() {
String name = this.getClass().getSimpleName();
System.out.println("name = " + name);
return name;
}
@Override
public void jobToBeExecuted(JobExecutionContext context) {
String name = context.getJobDetail().getKey().getName();
System.out.println("Job的名称是:" + name + " Scheduler在jobDetail将要被执行时调用的方法。");
}
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
System.out.println("xxx");
}
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
System.out.println("yyy");
}
}
2. 添加监听器
2.1 添加一个全局的监听器
在scheduler.start();之前加上如下代码,EverythingMatcher.allJobs()表示所有的job
scheduler.getListenerManager().addJobListener(new MyJobListener(),EverythingMatcher.allJobs());
2.2 添加一个局部监听器,监听指定的job
scheduler.getListenerManager().addJobListener(new MyJobListener(),KeyMatcher.keyEquals(JobKey.jobKey("job1", "group1")));
完整的调用代码:
package cn.bjc.quartz.main;
import java.util.Date;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.EverythingMatcher;
import org.quartz.impl.matchers.KeyMatcher;
import cn.bjc.quartz.job.HelloJobListener;
import cn.bjc.quartz.listener.MyJobListener;
public class JobListenerDemo {
public static void main(String[] args) throws Exception {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 2. 任务实例(JobDetail),通过JobBuilder创建
JobDetail jobDetail = JobBuilder.newJob(HelloJobListener.class)
.withIdentity("job1", "group1")
.usingJobData("msg", "日志打印")
.usingJobData("count", 0)
.build();
// 3. 触发器(Trigger)
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ?")) // 日历每秒执行
.build();
Date startDate = scheduler.scheduleJob(jobDetail, trigger);
// 创建一个全局的监听器
// scheduler.getListenerManager().addJobListener(new MyJobListener(),EverythingMatcher.allJobs());
// 创建一个局部监听器
scheduler.getListenerManager().addJobListener(new MyJobListener(),KeyMatcher.keyEquals(JobKey.jobKey("job1", "group1")));
// 启动
scheduler.start();
}
}
任务调度过程中,与触发器Trigger相关的事件包括:触发器触发、触发器未正常触发、触发器完成等。
1. getName():用于获取触发器名称
2. triggerField(Trigger trigger,JobExecutionContext context):当与监听器相关联的Trigger被触发,Job上的execute方法将被执行时,Scheduler就调用该方法。
3. vetoJobExecution(Trigger trigger,JobExecutionContext context):在Trigger触发后,job将要被执行时由Scheduler调用这个方法,TriggerListener给了一个选择去否决Job的执行。假如这个方法返回true,这个Job将不会为此次Trigger触发而得到执行。
4. triggerMisfired(Trigger trigger):Scheduler调用这个方法是在Trigger错过触发时,你应该关注此方法中持续时间长的逻辑;在出现许多错过触发的Trigger时,长逻辑会导致骨牌效应,所以,你应当尽量的保持这上方法尽量的小。
5. triggerComplete(Trigger trigger):Trigger被触发并且完成了Job的执行时,Scheduler调用这个方法。
1. 创建监听器
MyTriggerListener.java
package cn.bjc.quartz.listener;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
import org.quartz.Trigger;
import org.quartz.Trigger.CompletedExecutionInstruction;
import org.quartz.TriggerListener;
public class MyTriggerListener implements TriggerListener{
@Override
public String getName() {
String name = this.getClass().getSimpleName();
System.out.println("触发器的名称是:" + name);
return name;
}
@Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
String name = trigger.getKey().getName();
System.out.println(name + "被触发了!");
}
@Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
String name = trigger.getKey().getName();
System.out.println(name + "没有被触发!");
return true; // true:表示不会执行job的方法。
}
@Override
public void triggerMisfired(Trigger trigger) {
String name = trigger.getKey().getName();
System.out.println(name + "错过触发!");
}
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context,
CompletedExecutionInstruction triggerInstructionCode) {
String name = trigger.getKey().getName();
System.out.println(name + "完成之后触发!");
}
}
2. 添加监听器
2.1 添加一个全局监听器
// 创建并注册一个全局TriggerListener
scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(),EverythingMatcher.allTriggers());
2.2 添加一个局部监听器
// 创建并注册一个局部TriggerListener
scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1")));
完整的调用:
package cn.bjc.quartz.main;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.EverythingMatcher;
import org.quartz.impl.matchers.KeyMatcher;
import cn.bjc.quartz.job.HelloJobListener;
import cn.bjc.quartz.listener.MyJobListener;
import cn.bjc.quartz.listener.MyTriggerListener;
public class TriggerListenerDemo {
public static void main(String[] args) throws Exception {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 2. 任务实例(JobDetail),通过JobBuilder创建
JobDetail jobDetail = JobBuilder.newJob(HelloJobListener.class)
.withIdentity("job1", "group1")
.usingJobData("msg", "日志打印")
.usingJobData("count", 0)
.build();
// 3. 触发器(Trigger)
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ?")) // 日历每秒执行
.build();
Date startDate = scheduler.scheduleJob(jobDetail, trigger);
// 创建并注册一个全局TriggerListener
//scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(),EverythingMatcher.allTriggers());
// 创建并注册一个局部TriggerListener
scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1","group1")));
// 启动
scheduler.start();
}
}
SchedulerListener会在Scheduler的生命周期中关键事件发生时被调用,与Scheduler有关的事件包括:增加一个job/trigger,删除一个job/trigger,scheduler发生严重错误的时候,关闭scheduler。
1. jobScheduler(Trigger trigger):用于部署JobDetail时调用
2. jobUnscheduled(String triggerName,String triggerGroup):用于卸载JobDetail时调用
3. triggerFinalized(Trigger trigger):当一个trigger来到了再也不会触发的状态的时候调用这个方法。除非这个job已设置成了持久性,否则它就会从Scheduler中移除。
4. triggersPaused(String triggerName,String triggerGroup):Scheduler调用这个方法是发生在一个Trigger或trigger组被暂停的时候。假如Trigger组的话,triggerName参数将为null。
5. triggersResumed(String triggerName,String triggerGroup):Scheduler调用这个方法是发生在一个trigger或trigger组被暂停时。加入是trigger组的话,triggerName参数将为null
6. jobsPaused(String triggerName,String triggerGroup):当一个或一组job被暂停的时候调用
7. jobsResumed(String triggerName,String triggerGroup):当一个或一个组job从暂停上恢复时调用。加入是job组,jobName参数将为null。
8. schedulerError(String msg,SchedulerException cause):在Scheduler正常运行期间,产生一个严重错误的时候调用
9. schedulerStarted():当scheduler开启的时候调用
10. schedulerInStandbyMode():当scheduler处于StandBy模式时,调用该方法。
11. schedulerShutdown():当scheduler停止时调用
12. schedulingDataCleared():当scheduler中的数据被清除的时候调用
1. 自定义Scheduler监听器
package cn.bjc.quartz.listener;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.quartz.SchedulerListener;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
public class MySchedulerListener implements SchedulerListener{
@Override
public void jobScheduled(Trigger trigger) {
String name = trigger.getKey().getName();
System.out.println(name + "部署的时候调用!");
}
@Override
public void jobUnscheduled(TriggerKey triggerKey) {
String name = triggerKey.getName();
System.out.println(name + "卸载JobDetail时调用!");
}
@Override
public void triggerFinalized(Trigger trigger) {
String name = trigger.getKey().getName();
System.out.println(name + "被移除调用!");
}
@Override
public void triggerPaused(TriggerKey triggerKey) {
String name = triggerKey.getName();
System.out.println(name + "正在被暂停!");
}
@Override
public void triggersPaused(String triggerGroup) {
System.out.println("触发器组" + triggerGroup + "正在被暂停!");
}
@Override
public void triggerResumed(TriggerKey triggerKey) {
String name = triggerKey.getName();
System.out.println(name + "正在被恢复!");
}
@Override
public void triggersResumed(String triggerGroup) {
System.out.println("触发器组" + triggerGroup + "正在被恢复!");
}
@Override
public void jobAdded(JobDetail jobDetail) {
String name = jobDetail.getKey().getName();
System.out.println(name + "添加工作任务的时候调用!");
}
@Override
public void jobDeleted(JobKey jobKey) {
String name = jobKey.getName();
System.out.println(name + "删除工作任务的时候调用!");
}
@Override
public void jobPaused(JobKey jobKey) {
String name = jobKey.getName();
System.out.println(name + "暂停工作任务的时候调用!");
}
@Override
public void jobsPaused(String jobGroup) {
System.out.println("job组" + jobGroup + "暂停的时候调用!");
}
@Override
public void jobResumed(JobKey jobKey) {
System.out.println("工作任务恢复!");
}
@Override
public void jobsResumed(String jobGroup) {
System.out.println("工作组任务恢复!");
}
@Override
public void schedulerError(String msg, SchedulerException cause) {
System.out.println("调用出错,错误信息是:" + msg + " 具体错误原因是:" + cause.getUnderlyingException());
}
@Override
public void schedulerInStandbyMode() {
System.out.println("调度器被挂起模式的时候调用!");
}
@Override
public void schedulerStarted() {
System.out.println("调度器开启的时候调用!");
}
@Override
public void schedulerStarting() {
System.out.println("调度器正在开启的时候调用!");
}
@Override
public void schedulerShutdown() {
System.out.println("调度器关闭的时候调用!");
}
@Override
public void schedulerShuttingdown() {
System.out.println("调度器正在关闭的时候调用!");
}
@Override
public void schedulingDataCleared() {
System.out.println("当scheduler中的数据被清除的时候调用!");
}
}
2. 添加监听
scheduler.getListenerManager().addSchedulerListener(mySchedulerListener);
调用代码:
package cn.bjc.quartz.main;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import cn.bjc.quartz.job.HelloJobListener;
import cn.bjc.quartz.listener.MySchedulerListener;
public class SchedulerListenerDemo {
public static void main(String[] args) throws Exception {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 2. 任务实例(JobDetail),通过JobBuilder创建
JobDetail jobDetail = JobBuilder.newJob(HelloJobListener.class)
.withIdentity("job1", "group1")
.usingJobData("msg", "日志打印")
.usingJobData("count", 0)
.build();
// 3. 触发器(Trigger)
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)
.withRepeatCount(2)) // 5秒后重复执行,只重复执行4次
.build();
scheduler.scheduleJob(jobDetail, trigger);
MySchedulerListener mySchedulerListener = new MySchedulerListener();
// 注册监听器
scheduler.getListenerManager().addSchedulerListener(mySchedulerListener);
// 移除监听
// scheduler.getListenerManager().removeSchedulerListener(mySchedulerListener);
// 启动
scheduler.start();
Thread.sleep(8000);
scheduler.shutdown();
}
}