背景
为什么需要任务调度框架:
- 账单日或者还款日上午 10 点,给每个信用卡客户发送账单通知,还款通知。如何判断客户的账单日、还款日,完成通知的发送?
- 对于后台一些数据迁移、任务跑批等需要定时执行。
- …
类似基于这种:基于准确的时刻或者类似的时间间隔触发的任务、批量数据处理、要实现两个动作解耦的场景,我们都可以用任务调度来实现。
我们对任务调度框架的基本需求:
- 可以定义触发规则,比如基于时刻、时间间隔、表达式等。
- 可以定义需要执行的任务,比如执行一个脚本或者一段代码,任务和规则分开。
- 集中管理配置、持久化配置,不用把配置写在代码里面,可以看到所以任务配置,方便维护,重启之后任务可以再次调度。
- 支持任务的串行执行。
- 有自己调度器,可以运行、停止、中断任务。
- 容易集成到spring。
任务调度跨框架对比:
层次 | 举例 | 特点 |
---|---|---|
操作系统 | Linux Crontab、windows计划任务 | 只能执行简单脚本或者命令 |
数据库 | MySQL、Oracle | 可以操作数据,不能执行java代码 |
工具 | Kettle | 可以操作数据、执行脚本、没有集中配置 |
开发语言 | JDK Timer、 ScheduledThreadPool | Timer: 单线程,也就是任务的执行是串行的。JDK1.5 之后: ScheduledThreadPool(Cache、 Fiexed、Single) :没有集中配置, 日程管理不够灵活 |
容器 | Spring Task、 @Scheduled | 不支持集群、实现不了负载和高可用 |
分布式任务调度框架 | XXL-JOB, Elastic-Job |
quartz
Quartz 的意思是石英,像石英表一样精确。Quartz 是一个老牌的任务调度系统,98 年构思,01 年发布到 sourceforge。现在更新比较慢,因为已经非常成熟了。
官网:http://www.quartz-scheduler.org/
GitHub地址:https://github.com/quartz-scheduler/quartz
Quartz 的目的就是让任务调度更加简单,开发人员只需要关注业务即可。他是用 Java 语言编写的(也有.NET 的版本)。Java 代码能做的任何事情,Quartz 都可以调度。
特点:
- 精确到毫秒级别的调度。
- 可以独立运行,也可以集成到容器中。
- 支持事务(JobStoreCMT )。
- 支持集群。
- 支持持久化。
Quartz Java 编程
依赖:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
默认配置文件:
org.quartz包下,有一个默认的配置文件,quartz.properties。当我们没有定义一个同名的配置文件的时候,就会使用默认配置文件里面的配置。
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
Quartz体系结构:
JobDetail
我们创建一个实现 Job 接口的类,使用 JobBuilder 包装成 JobDetail,它可以携带
KV 的数据(通过JobDataMap)用于扩展属性,在运行的时候可以从 context获取到,然后通过name+group唯一确定一个JobDetail。
Trigger
定时任务的触发规则(触发器),定义了任务调度的触发时机,使用TriggerBuilder 构建,与JobDetail是N:1的关系,也就是一个JobDetail可以对应多个触发器。两者之间解耦,也就是任务与触发时机解耦,通过scheduler进行调控。
Trigger接口有四个子接口,代表四种类型的触发器。
子接口 | 描述 | 特点 |
---|---|---|
SimpleTrigger | 简单触发器 | 可以设置固定的时刻或者时间间隔触发,单位毫秒 |
CalendarIntervalTrigger | 基于日历的触发器 | 比简单触发器更多时间单位,支持非固定时间的触发, 例如一年可能 365/366, 一个月可能 28/29/30/31 |
DailyTimeIntervalTrigger | 基于日期的触发器 | 每天的某个时间段 |
CronTrigger | 基于cron表达式的触发器 | 基于cron表达式,十分的灵活,实际上可以代替上面的触发器 |
SimpleTrigger:
SimpleTrigger 可以定义固定时刻或者固定时间间隔的调度规则(精确到毫秒)。
CalendarIntervalTrigger:
CalendarIntervalTrigger 可以定义更多时间单位的调度需求,精确到秒。
好处是不需要去计算时间间隔,比如 1 个小时等于多少毫秒。
例如每年、每个月、每周、每天、每小时、每分钟、每秒。
每年的月数和每个月的天数不是固定的,这种情况也适用。
DailyTimeIntervalTrigger:
每天的某个时间段内,以一定的时间间隔执行任务。
例如:每天早上 9 点到晚上 9 点,每隔半个小时执行一次,并且只在周一到周六执行。
CronTrigger:
CronTirgger 可以定义基于 Cron 表达式的调度规则,是最常用的触发器类型。
Scheduler
调度器,是 Quartz 的指挥官,由 StdSchedulerFactory 产生。它是单例的。并且是 Quartz 中最重要的 API,默认是实现类是 StdScheduler,里面包含了一个QuartzScheduler。QuartzScheduler 里面又包含了一个 QuartzSchedulerThread(调度线程池)。
Scheduler中的方法分为三大类:
1)操作调度器本身,例如调度器的启动 start()、调度器的关闭 shutdown()。
2)操作 Trigger,例如 pauseTriggers()、resumeTrigger()。
3)操作 Job,例如 scheduleJob()、unscheduleJob()、rescheduleJob()
Listener
我们有这么一种需求,在每个任务运行结束之后发送通知给运维管理员。那是不是要在每个任务的最后添加一行代码呢?这种方式对原来的代码造成了入侵,不利于维护。如果代码不是写在任务代码的最后一行,怎么知道任务执行完了呢?或者说,怎么监测到任务的生命周期呢?那就需要Listener来监控任务的生命周期。
Quartz 中提供了三种 Listener,监听 Scheduler 的,监听 Trigger 的,监听 Job 的。
只需要创建类实现相应的接口,并在 Scheduler 上注册 Listener,便可实现对核心对象的监听。
JobStore
Jobstore 用来存储任务和触发器相关的信息,例如所有任务的名称、数量、状态等等。Quartz 中有两种存储任务的方式,一种在在内存(RAMJobStore),一种是在数据库(JDBCJobStore)。
实战
//任务的定义
/**
* @author YeHaocong
* @decription 任务定义、实现Job接口,重写execute方法,这个就是任务调度时执行的逻辑。
*/
public class MyJobDemo1 implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//JobExecutionContext是上下文,可以获取KV信息、触发器等信息。
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
JobDataMap dataMap = jobExecutionContext.getJobDetail().getJobDataMap();
System.out.println( " " + sf.format(date) + " 任务1执行了," + dataMap.getString("jobName"));
}
}
//触发器的定义
//触发器的类型要靠ScheduleBuilder的类型来区分
//创建一个SimpleTrigger
public Trigger getSimpleTrigger(){
Trigger simpleTrigger = TriggerBuilder.newTrigger()
//设置开始时间
.startAt(new Date())
//设置额外信息
.usingJobData("tName","simpleTrigger")
//设置触发器的id
.withIdentity("simpleTrigger","triggerGroup")
//描述
.withDescription("这是简单触发器")
//设置触发条件,这里使用SimpleScheduleBuilder,意味着创建的是simpleTrigger,触发条件是3秒一次且永远重复
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3))
//立即启用触发器
.startNow()
//创建
.build();
return simpleTrigger;
}
//创建一个CalendarIntervalTrigger
public Trigger getCalendarIntervalTrigger(){
Trigger calendarIntervalTrigger = TriggerBuilder.newTrigger()
//定义额外参数
.usingJobData("tName","CalendarIntervalTrigger")
//使用name和group唯一确定这个触发器
.withIdentity("CalendarIntervalTrigger","triggerGroup")
//描述
.withDescription("这是基于日历的触发器")
//每分钟执行一次
.withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withIntervalInMinutes(1))
//立即启动
.startNow()
.build();
return calendarIntervalTrigger;
}
//创建一个DailyTimeIntervalTrigger
public Trigger getDailyTimeIntervalTrigger(){
Trigger dailyTimeIntervalTrigger = TriggerBuilder.newTrigger()
//设置为星期六星期天
.withSchedule(DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule().onSaturdayAndSunday())
.build();
return dailyTimeIntervalTrigger;
}
//创建cronTrigger
public Trigger getCronTrigger(){
Trigger dailyTimeIntervalTrigger = TriggerBuilder.newTrigger()
//设置为每5秒执行一次
.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?"))
.build();
return dailyTimeIntervalTrigger;
}
//使用scheduler来实现一次任务调度
/**
* @author YeHaocong
* @decription TODO
*/
public class MyScheduler {
public static void main(String[] args) throws SchedulerException {
//创建任务
JobDetail jobDetail = JobBuilder.newJob(MyJobDemo1.class)
.usingJobData("jobName","MyJobDemo1")
.withIdentity("MyJobDemo1","jobGroup")
.build();
//创建简单触发器。每分钟执行一次
Trigger simpleTrigger = TriggerBuilder.newTrigger()
//定义额外参数
.usingJobData("tName","simpleTrigger")
//使用name和group唯一确定这个触发器
.withIdentity("simpleTrigger","triggerGroup")
//描述
.withDescription("简单触发器")
//每分钟执行一次
.withSchedule(SimpleScheduleBuilder.repeatMinutelyForever())
//立即启动
.startNow()
.build();
//创建CalendarIntervalTrigger,每两分钟执行一次
Trigger CalendarIntervalTrigger = TriggerBuilder.newTrigger()
//定义额外参数
.usingJobData("tName","CalendarIntervalTrigger")
//使用name和group唯一确定这个触发器
.withIdentity("CalendarIntervalTrigger","triggerGroup")
//.forJob(jobDetail)
//.forJob(jobDetail2)
//描述
.withDescription("这是基于日历的触发器")
//每2分钟执行一次
.withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withIntervalInMinutes(2))
//立即启动
.startNow()
.build();
//把两个触发器加入到一个集合里面
Set<Trigger> triggers = new HashSet<>();
triggers.add(CalendarIntervalTrigger);
triggers.add(simpleTrigger);
//创建一个调度器工厂
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
//获取调度器,单例模式的,获取的调度器都一样
Scheduler scheduler = schedulerFactory.getScheduler();
//添加任务和触发器 这个就是 1个任务对应2个触发器 第三个参数是是否代替的意思,就是如果存在同名、同组任务或者触发器时,是否用这个覆盖。
scheduler.scheduleJob(jobDetail,triggers,false);
//启动任务调度
scheduler.start();
}
}
结果:
18.40.19因为两个触发器都执行了,18.41.19是简单触发器触发,因为他是一分钟间隔。18.42.19两个触发,合情合理。
基于 Calendar 的排除规则
如果要在触发器的基础上,排除一些时间区间(比如圣诞节、春节等等)不执行任务,就要用到 Quartz 的Calendar 类(注意不是 JDK 的 Calendar)。可以按年、月、周、日、特定日期、Cron表达式排除。
- BaseCalendar:为高级的 Calendar 实现了基本的功能, 实现了 org.quartz.Calendar 接口。
- AnnualCalendar:排除年中一天或多天
- CronCalendar :cron的这种实现排除了由给定的 CronExpression 表达的时间集合。 比如使用* * 0-7,18-23 ? * * 就排除每天0-7 18-23 这段时间,只在8-17这段时间剩下。如果 CronTrigger 具有给定的 cron 表达式并且与具有相同表达式的 CronCalendar 相冲突, 则将排除触发器包含的所有时间, 并且它们将彼此抵消。也就是假如排除每天8-15时,然后CronTrigger 设置的规则是8-16时每五秒执行一次,最后的结果是16时每5秒执行一次,8-15被排除了。取CronTrigger 的补集。
- DailyCalendar:可以使用此日历来排除营业时间(上午 8 点 - 5 点) 每天。 每个DailyCalendar 仅允许指定单个时间范围, 并且该时间范围可能不会跨越每日边界(即, 不能指定从上午 8 点至凌晨 5 点的时间范围) 。 如果属性 invertTimeRange 为 false(默认) , 则时间范围定义触发器不允许触发
的时间范围。 如果 invertTimeRange 为 true, 则时间范围被反转 - 也就是排除在定义的时间范围之外的所有时间。 - HolidayCalendar:特别的用于从 Trigger 中排除节假日。
- MonthlyCalendar :排除月份中的指定数天, 例如, 可用于排除每月的最后一天。
- WeeklyCalendar:排除星期中的任意周几, 例如, 可用于排除周末, 默认周六和周日
调用 Trigger 的 modifiedByCalendar()添加到触发器中,并且调用调度器的addCalendar()方法注册排除规则。
modifiedByCalendar只能一次,也就是一个触发器只能modifiedByCalendar一个Calendar,否则如果多个,后面会覆盖前面,最终只有最后一个modifiedByCalendar的Calendar起效果。
public class MyScheduler2 {
public static void main(String[] args) throws SchedulerException {
AnnualCalendar annualCalendar = new AnnualCalendar();
//定义国庆节
Calendar calendar = new GregorianCalendar(2021,10,1);
//排除国庆节
annualCalendar.setDayExcluded(calendar,true);
//排除8-17点
DailyCalendar dailyCalendar = new DailyCalendar("08:00:00","17:59:59");
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.addCalendar("annualCalendar",annualCalendar,false,false);
JobDetail jobDetail = JobBuilder.newJob(MyJobDemo1.class)
.withIdentity("job1", "group1")
.usingJobData("name","job")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
//添加规则到触发器
.modifiedByCalendar("annualCalendar")
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2)
.repeatForever())
.build();
}
}
Listener
JobListener
首先定义一个类实现JobListener,并实现他的生命周期方法。并用ListenerManager对象对监听器进行添加、移除、获取。
Matcher,主要是基于 groupName 和 keyName 进行匹配。
//实现的listener类
public class MyJobListener implements JobListener {
@Override
public String getName() {
String name = getClass().getSimpleName();
System.out.println( "Method 111111 :"+ "获取到监听器名称:"+name);
return name;
}
@Override
//任务即将执行时调用
public void jobToBeExecuted(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().getName();
System.out.println("Method 222222 :"+ jobName + " ——任务即将执行 ");
}
@Override
//Scheduler 在 JobDetail 即将被执行, 但又被 TriggerListener 否决了时调用这个
方法
public void jobExecutionVetoed(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().getName();
System.out.println("Method 333333 :"+ jobName + " ——任务被否决 ");
}
@Override
//任务执行完毕时调用
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
String jobName = context.getJobDetail().getKey().getName();
System.out.println("Method 444444 :"+ jobName + " ——执行完毕 ");
System.out.println("------------------");
}
}
//测试类
public class JobListenerTest {
public static void main(String[] args) throws SchedulerException {
// JobDetail
JobDetail jobDetail = JobBuilder.newJob(MyJobDemo1.class).withIdentity("job1", "group1").build();
// Trigger
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
// SchedulerFactory
SchedulerFactory factory = new StdSchedulerFactory();
// Scheduler
Scheduler scheduler = factory.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
// 创建并注册一个全局的Job Listener
scheduler.getListenerManager().addJobListener(new MyJobListener(), EverythingMatcher.allJobs());
scheduler.start();
}
}
TriggerListener
这里接不写代码了,直接说明生命周期方法。
方法 | 描述 |
---|---|
getName() | 获取监听器名称 |
triggerFired() | Trigger 被触发, Job 上的 execute() 方法将要被执行时, Scheduler 就调用这个方法 |
vetoJobExecution() | 在 Trigger 触 发 后 , Job 将 要 被 执 行 时 由 Scheduler 调 用 这 个 方 法 。TriggerListener 给了一个选择去否决 Job 的执行。 假如这个方法返回 true, 这个 Job 将不会为此次 Trigger 触发而得到执行,就是如果该方法返回true,代表这个任务不会被执行 |
triggerMisfired() | Trigger 错过触发时调用 |
triggerComplete() | Trigger 被触发并且完成了 Job 的执行时, Scheduler 调用这个方法 |
SchedulerListener
方法 | 描述 |
---|---|
jobScheduled(Trigger trigger) | 调用scheduledJob方法成功的回调 |
jobUnscheduled(TriggerKey triggerKey) | 调用unscheduleJob方法成功的回调 |
triggerFinalized(Trigger trigger) | 触发器触发完成的回调 |
triggerPaused(TriggerKey triggerKey) | 调用pauseTrigger方法成功的回调 |
triggersPaused(String triggerGroup) | 调用pauseTriggers方法成功的回调 |
triggerResumed(TriggerKey triggerKey) | 调用resumeTrigger方法成功的回调 |
triggersResumed(String triggerGroup) | 调用resumeTriggers方法成功的回调 |
jobAdded(JobDetail jobDetail) | 调用addJob方法成功的回调 |
jobDeleted(JobKey jobKey) | 调用deleteJob方法成功的回调 |
jobPaused(JobKey jobKey) | 调用pauseJob方法成功的回调 |
jobsPaused(String jobGroup) | 调用pauseJobs方法成功的回调 |
jobResumed(JobKey jobKey) | 调用resumeJob方法成功的回调 |
jobsResumed(String jobGroup) | 调用resumeJobs方法成功的回调 |
schedulerError(String msg, SchedulerException cause) | scheduler调用方法出现异常时的回调 |
schedulerInStandbyMode() | 调用standby方法成功的回调 |
schedulerStarted() | scheduler启动后的回调 |
schedulerStarting() | scheduler启动时的回调 |
schedulerShutdown() | scheduler关闭的回调 |
schedulerShuttingdown() | scheduler关闭时的回调 |
schedulingDataCleared() | 调用clear方法的回调 |
API列表
TriggerBuilder:
//这个类用于构建Trigger
//创建一个TriggerBuilder
public static TriggerBuilder<Trigger> newTrigger()
//最终创建一个触发器
public T build()
//定义触发器名字,group使用null。
public TriggerBuilder<T> withIdentity(String name)
//配置触发器name和group,这两个唯一确定一个触发器
public TriggerBuilder<T> withIdentity(String name, String group)
//使用TriggerKey 配置触发器的name和group,TriggerKey 是对name和group的封装
public TriggerBuilder<T> withIdentity(TriggerKey triggerKey)
//定义触发器的描述信息
public TriggerBuilder<T> withDescription(String triggerDescription)
//触发器的优先级,如果多个触发器在同一时间触发,优先给优先级高的触发器分配线程调度。数字越大,优先级越高
public TriggerBuilder<T> withPriority(int triggerPriority)
//添加Calendar日期规则,要配合scheduler的addCalendar()使用,也就是Trigger添加的Calendar必须要在scheduler的addCalendar()添加过。
public TriggerBuilder<T> modifiedByCalendar(String calName)
//从什么时间开始
public TriggerBuilder<T> startAt(Date triggerStartTime)
//立即启用调度器
public TriggerBuilder<T> startNow()
//从什么时候结束,比如从8点开始、10点结束,期间每10分钟执行一次。
public TriggerBuilder<T> endAt(Date triggerEndTime)
//设置调度规则,ScheduleBuilder的类型配合TriggerBuilder的泛型决定Trigger的类型。
public <SBT extends T> TriggerBuilder<SBT> withSchedule(ScheduleBuilder<SBT> schedBuilder)
//添加任务,可以执行多次,但是只有最后一个生效,因为任务和触发器时一对多,一个触发器只能对应一个任务。
//还有一些重载方法,这里就不列出来了。
//这里添加的任务必须要在调度器scheduler中调用addJob添加的任务列表内才行,不然报错。
public TriggerBuilder<T> forJob(JobKey keyOfJobToFire)
//添加k/V参数到触发器,可调用多次。其他重载方法就不列出来了
public TriggerBuilder<T> usingJobData(String dataKey, String value)
ScheduleBuilder:
//调度规则构建器,有四个实现类,分别代表四种触发器。
//CalendarIntervalScheduleBuilder:CalendarIntervalTrigger
//CronScheduleBuilder:CronTrigger
//DailyTimeIntervalScheduleBuilder:DailyTimeIntervalTrigger
//SimpleScheduleBuilder:SimpleTrigger
//这四个的具体API就省略了,因为十分能见名知意,根据方法名就能知道啥意思。
JobBuilder:
//该类用于构建JobDetail
//创建一个JobBuilder 对象
public static JobBuilder newJob()
//newJob()+ofType 创建一个JobBuilder 并指定任务的类型,也就是我们实现了Job的自定义类对象。
public static JobBuilder newJob(Class <? extends Job> jobClass)
//最终创建一个JobDetail
public JobDetail build()
//设置任务身份信息,group为null。
public JobBuilder withIdentity(String name)
//设置任务身份信息,设置name+group
public JobBuilder withIdentity(String name, String group)
//设置任务身份信息,JobKey 是name和group的封装。
public JobBuilder withIdentity(JobKey jobKey)
//设置描述信息
public JobBuilder withDescription(String jobDescription)
//设置JobDetail的任务类型
public JobBuilder ofType(Class <? extends Job> jobClazz)
//指示调度器,当任务调用过程中出现故障转移或者恢复时,是否重新执行任务,默认false,这个方法就是把它设置为true。
public JobBuilder requestRecovery()
//直接指定true或者false。
public JobBuilder requestRecovery(boolean jobShouldRecover)
//当该任务没有被触发器指定时或者跟触发器绑定时,是否进行持久化,默认false,这个方法设置为true。
public JobBuilder storeDurably()
//这个方法可以设置true和false。
public JobBuilder storeDurably(boolean jobDurability)
//配置额外KV信息,其他重载方法不列出。
public JobBuilder usingJobData(String dataKey, String value)
//usingJobData是在原本的基础上添加,这个方法是直接替换原本的。
public JobBuilder setJobData(JobDataMap newJobDataMap)
scheduler:
//调度器,赋值调控trigger和job,使得他们解耦
//获取调度器名称
String getSchedulerName()
//获取调度器id
String getSchedulerInstanceId()
//获取调度器的上下文,包括调控的触发器、任务等等信息。
SchedulerContext getContext()
//启动
void start()
//调度器延迟多少秒后才启动
void startDelayed(int seconds)
//返回调度器是否已经启动
boolean isStarted()
//把调度器状态设置为就绪状态,相当于停止状态,此状态的调度器不会工作。此状态下的调度器可以随时重新启动工作。
//该状态下的调度器会记录在该状态期间理应触发的触发器,但是因为在该状态下,不能工作,所以会把它们记录下来,等待重新启动时会补执行。
void standby()
//返回是否调度器是否处于standby状态。
boolean isInStandbyMode()
//关闭调度器,关闭后就不能再次重新启动。默认不等待任务执行完成,直接返回。
void shutdown()
//关闭调度器,waitForJobsToComplete如果设置为true,那么该方法会阻塞直到执行完全部正在执行的任务后再返回。上面的 shutdown()的waitForJobsToComplete为false。直接返回。
void shutdown(boolean waitForJobsToComplete)
//返回是否已关闭
boolean isShutdown()
//获取调度器元信息
SchedulerMetaData getMetaData()
//获取当前正在执行的任务列表。
getCurrentlyExecutingJobs()
//设置JobFactory
void setJobFactory(JobFactory factory)
//获取该调度器的监听器管理器,用于添加、移除、获取监听器
ListenerManager getListenerManager()
//调度任务1对1
Date scheduleJob(JobDetail jobDetail, Trigger trigger)
//调度任务,配合addjob + trigger.forjob使用。
Date scheduleJob(Trigger trigger)
//调度任务,1对多,一个任务对多个触发器,replace表示存在同名触发器或者任务时,是否覆盖。
void scheduleJobs(Map<JobDetail, Set<? extends Trigger>> triggersAndJobs, boolean replace)
//调度任务1对多
void scheduleJob(JobDetail jobDetail, Set<? extends Trigger> triggersForJob, boolean replace)
//移除触发器
boolean unscheduleJob(TriggerKey triggerKey)
//移除多个触发器
boolean unscheduleJobs(List<TriggerKey> triggerKeys)
//移除triggerKey触发器,设置newTrigger触发器
Date rescheduleJob(TriggerKey triggerKey, Trigger newTrigger)
//添加任务,replace同名是否覆盖
void addJob(JobDetail jobDetail, boolean replace)
//添加任务,storeNonDurableWhileAwaitingScheduling
void addJob(JobDetail jobDetail, boolean replace, boolean storeNonDurableWhileAwaitingScheduling)
//删除任务
boolean deleteJob(JobKey jobKey)
//删除多个任务
boolean deleteJobs(List<JobKey> jobKeys)
//立即执行某个任务
void triggerJob(JobKey jobKey)
//立即执行某个任务,可以设置额外信息
void triggerJob(JobKey jobKey, JobDataMap data)
//停止任务,通过停止他所有的关联触发器实现的。
void pauseJob(JobKey jobKey)
//通过一定组规则停止任务
void pauseJobs(GroupMatcher<JobKey> matcher)
//停止触发器
void pauseTrigger(TriggerKey triggerKey)
//通过一定组规则停止触发器
void pauseTriggers(GroupMatcher<TriggerKey> matcher)
//重新启动被 pause的任务,会记录在停止期间错过的执行次数,恢复时会补执行。
void resumeJob(JobKey jobKey)
//根据组规则恢复任务
void resumeJobs(GroupMatcher<JobKey> matcher)
//恢复触发器,会记录在停止期间错过的执行次数,恢复时会补执行。
void resumeTrigger(TriggerKey triggerKey)
//根据组规则恢复触发器
void resumeTriggers(GroupMatcher<TriggerKey> matcher)
//停止所有触发器
void pauseAll()
//恢复所有触发器
void resumeAll()
//获取任务组名列表
List<String> getJobGroupNames()
//获取某个组的任务集合
Set<JobKey> getJobKeys(GroupMatcher<JobKey> matcher)
//获取任务关联的触发器列表
List<? extends Trigger> getTriggersOfJob(JobKey jobKey)
//获取某个组的触发器列表
List<String> getTriggerGroupNames()
//获取被停止的触发器组集合
Set<String> getPausedTriggerGroups()
//获取JobDetail
JobDetail getJobDetail(JobKey jobKey)
//获取触发器
Trigger getTrigger(TriggerKey triggerKey)
//获取触发器状态。
//六个状态NONE, NORMAL(正常状态), PAUSED(停止状态), COMPLETE(已完成), ERROR(出错), BLOCKED(阻塞)
TriggerState getTriggerState(TriggerKey triggerKey)
//将指定触发器把状态从ERROR改为NORMAL或者PAUSED
void resetTriggerFromErrorState(TriggerKey triggerKey)
//
//添加日期规则,replace同名是否覆盖,updateTriggers是否更新已经有了日期规则的触发器,使其使用新添加的规则。
void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers)
//删除日期规则
boolean deleteCalendar(String calName)
//根据名字获取规则
Calendar getCalendar(String calName)
//获取日期规则列表
List<String> getCalendarNames()
//中断某个任务
boolean interrupt(JobKey jobKey)
//返回任务是否存在
boolean checkExists(JobKey jobKey)
//返回触发器是否存在
boolean checkExists(TriggerKey triggerKey)
//清除所有东西,包括触发器、任务、日期规则等等。
void clear()
持久化
Jobstore 用来存储任务和触发器相关的信息,例如所有任务的名称、数量、状态等等。Quartz 中有两种存储任务的方式,一种在在内存,一种是在数据库。
RAMJobStore
quartz 默认的 JobStore 是 RAMJobstore,也就是把任务和触发器信息运行的信息存储在内存中,用到了 HashMap、TreeSet、HashSet 等等数据结构。如果程序崩溃或重启,所有存储在内存中的数据都会丢失。所以我们需要把这些数据持久化到磁盘。
JDBCJobStore
JDBCJobStore 可以通过 JDBC 接口,将任务运行数据保存在数据库中。
JDBC 的实现方式有两种,JobStoreSupport 类的两个子类:
JobStoreTX:在独立的程序中使用(专门的任务调度系统),自己管理事务,不参与外部事务。
JobStoreCMT:(Container Managed Transactions (CMT),如果需要容器管理事务时,使用它。
需要配置数据库信息,在类路径的quartz.properties文件中配置。
#使用的JobStoreTX实现
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
#指定的代理类
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 使用 quartz.properties, 不使用默认配置
org.quartz.jobStore.useProperties:true
#数据库中 quartz 表的表名前缀
org.quartz.jobStore.tablePrefix:QRTZ_
#数据源 ,在下面配置
org.quartz.jobStore.dataSource:myDS
#配置数据源
org.quartz.dataSource.myDS.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL:jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf8
org.quartz.dataSource.myDS.user:root
org.quartz.dataSource.myDS.password:123456
org.quartz.dataSource.myDS.validationQuery=select 0 from dual
表结构从jar包的org.quartz.impl.jdbcjobstore包中可以获得:我们选择mysql相关建表语句。
表的含义:
表名 | 作用 |
---|---|
QRTZ_BLOB_TRIGGERS | Trigger 作为 Blob 类型存储 |
QRTZ_CALENDARS | 存储 Quartz 的 Calendar 信息 |
QRTZ_CRON_TRIGGERS | 存储 CronTrigger, 包括 Cron 表达式和时区信息 |
QRTZ_FIRED_TRIGGERS | 存储与已触发的 Trigger 相关的状态信息, 以及相关 Job 的执行信息 |
QRTZ_JOB_DETAILS | 存储每一个已配置的 Job 的详细信息 |
QRTZ_LOCKS | 存储程序的悲观锁的信息 |
QRTZ_PAUSED_TRIGGER_GRPS | 存储已暂停的 Trigger 组的信息 |
QRTZ_SCHEDULER_STATE | 存储少量的有关 Scheduler 的状态信息, 和别的 Scheduler 实例 |
QRTZ_SIMPLE_TRIGGERS | 存储 SimpleTrigger 的信息, 包括重复次数、 间隔、 以及已触的次数 |
QRTZ_SIMPROP_TRIGGERS | 存储 CalendarIntervalTrigger 和 DailyTimeIntervalTrigger 两种类型的触发器 |
QRTZ_TRIGGERS | 存储已配置的 Trigger 的信息 |
验证:
执行一次调度后,就把相关信息持久化到数据库的表中。
与spring的集成
就是配置bean而已,Spring 在 spring-context-support.jar 中直接提供了对 Quartz 的支持。
如果是基于xml配置的话:需要配置以下对象代替原本的。
org.springframework.scheduling.quartz.JobDetailFactoryBean //JobDetail
org.springframework.scheduling.quartz.SimpleTriggerFactoryBean //简单触发器
org.springframework.scheduling.quartz.SchedulerFactoryBean //调度器
如果使用的是java配置类的话,就跟使用原生那样配置就可以了。