任务调度框架
Quartz
文档
概述
各种企业应用几乎都会碰到任务调度的需求,就拿论坛来说:每隔半个小时生成精华文章的
RSS
文件,每天凌晨统计论坛用户的积分排名,每隔
30
分钟执行锁定用户解锁任务。
对于一个典型的
MIS
系统来说,在每月
1
号凌晨统计上个月各部门的业务数据生成月报表,每半个小时查询用户是否已经有快到期的待处理业务
……
,这样的例子俯拾皆是,不胜枚举。
任务调度本身涉及到多线程并发、运行时间规则制定和解析、场景保持与恢复、线程池维护等诸多方面的工作。如果直接使用自定义线程这种刀耕火种的原始办法,开发任务调度程序是一项颇具挑战性的工作。
Java
开源的好处就是:领域问题都能找到现成的解决方案。
OpenSymphony
所提供的
Quartz
自
2001
年发布版本以来已经被众多项目作为任务调度的解决方案,
Quartz
在提供巨大灵活性的同时并未牺牲其简单性,它所提供的强大功能使你可以应付绝大多数的调度需求。
Quartz
在开源任务调度框架中的翘首,它提供了强大任务调度机制,难能可贵的是它同时保持了使用的简单性。
Quartz
允许开发人员灵活地定义触发器的调度时间表,并可以对触发器和任务进行关联映射。
此外,
Quartz
提供了调度运行环境的持久化机制,可以保存并恢复调度现场,即使系统因故障关闭,任务调度现场数据并不会丢失。此外,
Quartz
还提供了组件式的侦听器、各种插件、线程池等功能。
了解
Quartz
体系结构
Quartz
对任务调度的领域问题进行了高度的抽象,提出了调度器、任务和触发器这
3
个核心的概念,并在
org.quartz
通过接口和类对重要的这些核心概念进行描述:
●Job
:是一个接口,只有一个方法
void
execute(JobExecutionContext context)
,开发者实现该接口定义运行任务,
JobExecutionContext
类提供了调度上下文的各种信息。
Job
运行时的信息保存在
JobDataMap
实例中;
●JobDetail
:
Quartz
在每次执行
Job
时,都重新创建一个
Job
实例,所以它不直接接受一个
Job
的实例,相反它接收一个
Job
实现类,以便运行时通过
newInstance()
的反射机制实例化
Job
。因此需要通过一个类来描述
Job
的实现类及其它相关的静态信息,如
Job
名字、描述、关联监听器等信息,
JobDetail
承担了这一角色。
通过该类的构造函数可以更具体地了解它的功用:
JobDetail(java.lang.String
name, java.lang.String group, java.lang.Class jobClass)
,该构造函数要求指定
Job
的实现类,以及任务在
Scheduler
中的组名和
Job
名称;
●Trigger
:是一个类,描述触发
Job
执行的时间触发规则。主要有
SimpleTrigger
和
CronTrigger
这两个子类。当仅需触发一次或者以固定时间间隔周期执行,
SimpleTrigger
是最适合的选择;而
CronTrigger
则可以通过
Cron
表达式定义出各种复杂时间规则的调度方案:如每早晨
9:00
执行,周一、周三、周五下午
5:00
执行等;
●Calendar
:
org.quartz.Calendar
和
java.util.Calendar
不同,它是一些日历特定时间点的集合(可以简单地将
org.quartz.Calendar
看作
java.util.Calendar
的集合
——java.util.Calendar
代表一个日历时间点,无特殊说明后面的
Calendar
即指
org.quartz.Calendar
)。一个
Trigger
可以和多个
Calendar
关联,以便排除或包含某些时间点。
假设,我们安排每周星期一早上
10:00
执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在
Trigger
触发机制的基础上使用
Calendar
进行定点排除。针对不同时间段类型,
Quartz
在
org.quartz.impl.calendar
包下提供了若干个
Calendar
的实现类,如
AnnualCalendar
、
MonthlyCalendar
、
WeeklyCalendar
分别针对每年、每月和每周进行定义;
●Scheduler
:代表一个
Quartz
的独立运行容器,
Trigger
和
JobDetail
可以注册到
Scheduler
中,两者在
Scheduler
中拥有各自的组及名称,组及名称是
Scheduler
查找定位容器中某一对象的依据,
Trigger
的组及名称必须唯一,
JobDetail
的组和名称也必须唯一(但可以和
Trigger
的组和名称相同,因为它们是不同类型的)。
Scheduler
定义了多个接口方法,允许外部通过组及名称访问和控制容器中
Trigger
和
JobDetail
。
Scheduler
可以将
Trigger
绑定到某一
JobDetail
中,这样当
Trigger
触发时,对应的
Job
就被执行。一个
Job
可以对应多个
Trigger
,但一个
Trigger
只能对应一个
Job
。可以通过
SchedulerFactory
创建一个
Scheduler
实例。
Scheduler
拥有一个
SchedulerContext
,它类似于
ServletContext
,保存着
Scheduler
上下文信息,
Job
和
Trigger
都可以访问
SchedulerContext
内的信息。
SchedulerContext
内部通过一个
Map
,以键值对的方式维护这些上下文数据,
SchedulerContext
为保存和获取数据提供了多个
put()
和
getXxx()
的方法。可以通过
Scheduler# getContext()
获取对应的
SchedulerContext
实例;
●ThreadPool
:
Scheduler
使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。
Job
有一个
StatefulJob
子接口,代表有状态的任务,该接口是一个没有方法的标签接口,其目的是让
Quartz
知道任务的类型,以便采用不同的执行方案。无状态任务在执行时拥有自己的
JobDataMap
拷贝,对
JobDataMap
的更改不会影响下次的执行。而有状态任务共享共享同一个
JobDataMap
实例,每次任务执行对
JobDataMap
所做的更改会保存下来,后面的执行可以看到这个更改,也即每次执行任务后都会对后面的执行发生影响。
正因为这个原因,无状态的
Job
可以并发执行,而有状态的
StatefulJob
不能并发执行,这意味着如果前次的
StatefulJob
还没有执行完毕,下一次的任务将阻塞等待,直到前次任务执行完毕。有状态任务比无状态任务需要考虑更多的因素,程序往往拥有更高的复杂度,因此除非必要,应该尽量使用无状态的
Job
。
如果
Quartz
使用了数据库持久化任务调度信息,无状态的
JobDataMap
仅会在
Scheduler
注册任务时保持一次,而有状态任务对应的
JobDataMap
在每次执行任务后都会进行保存。
Trigger
自身也可以拥有一个
JobDataMap
,其关联的
Job
可以通过
JobExecutionContext#getTrigger().getJobDataMap()
获取
Trigger
中的
JobDataMap
。不管是有状态还是无状态的任务,在任务执行期间对
Trigger
的
JobDataMap
所做的更改都不会进行持久,也即不会对下次的执行产生影响。
Quartz
拥有完善的事件和监听体系,大部分组件都拥有事件,如任务执行前事件、任务执行后事件、触发器触发前事件、触发后事件、调度器开始事件、关闭事件等等,可以注册相应的监听器处理感兴趣的事件。
图
1
描述了
Scheduler
的内部组件结构,
SchedulerContext
提供
Scheduler
全局可见的上下文信息,每一个任务都对应一个
JobDataMap
,虚线表达的
JobDataMap
表示对应有状态的任务:
图
1 Scheduler
结构图
一个
Scheduler
可以拥有多个
Triger
组和多个
JobDetail
组,注册
Trigger
和
JobDetail
时,如果不显式指定所属的组,
Scheduler
将放入到默认组中,默认组的组名为
Scheduler.DEFAULT_GROUP
。组名和名称组成了对象的全名,同一类型对象的全名不能相同。
Scheduler
本身就是一个容器,它维护着
Quartz
的各种组件并实施调度的规则。
Scheduler
还拥有一个线程池,线程池为任务提供执行线程
——
这比执行任务时简单地创建一个新线程要拥有更高的效率,同时通过共享节约资源的占用。通过线程池组件的支持,对于繁忙度高、压力大的任务调度,
Quartz
将可以提供良好的伸缩性。
提示:
Quartz
完整下载包
examples
目录下拥有
10
多个实例,它们是快速掌握
Quartz
应用很好的实例。
使用
SimpleTrigger
SimpleTrigger
拥有多个重载的构造函数,用以在不同场合下构造出对应的实例:
●SimpleTrigger(String name, String group)
:通过该构造函数指定
Trigger
所属组和名称;
●SimpleTrigger(String name, String group, Date startTime)
:除指定
Trigger
所属组和名称外,还可以指定触发的开发时间;
●SimpleTrigger(String name, String group, Date startTime,
Date endTime, int repeatCount, long repeatInterval)
:除指定以上信息外,还可以指定结束时间、重复执行次数、时间间隔等参数;
●SimpleTrigger(String name, String group, String jobName,
String jobGroup, Date startTime, Date endTime, int repeatCount, long
repeatInterval)
:这是最复杂的一个构造函数,在指定触发参数的同时,还通过
jobGroup
和
jobName
,让该
Trigger
和
Scheduler
中的某个任务关联起来。
通过实现
org.quartz..Job
接口,可以使
Java
类化身为可调度的任务。代码清单
1
提供了
Quartz
任务的一个示例:
代码清单
1 SimpleJob
:简单的
Job
实现类
package com.baobaotao.basic.quartz;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class SimpleJob implements Job {
①
实例
Job
接口方法
public void
execute(JobExecutionContext jobCtx)
throws JobExecutionException {
System.out.println(jobCtx.getTrigger().getName()+ "
triggered. time is:" + (new Date()));
}
}
这个类用一条非常简单的输出语句实现了
Job
接口的
execute(JobExecutionContext context)
方法,这个方法可以包含想要执行的任何代码。下面,我们通过
SimpleTrigger
对
SimpleJob
进行调度:
代码清单
2 SimpleTriggerRunner
:使用
SimpleTrigger
进行调度
package com.baobaotao.basic.quartz;
import java.util.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
public class SimpleTriggerRunner {
public static void main(String args[]) {
try {
①
创建一个
JobDetail
实例,指定
SimpleJob
JobDetail jobDetail = new
JobDetail("job1_1","jGroup1",
SimpleJob.class
);
②
通过
SimpleTrigger
定义调度规则:马上启动,每
2
秒运行一次,共运行
100
次
SimpleTrigger simpleTrigger = new
SimpleTrigger("trigger1_1","tgroup1");
simpleTrigger.setStartTime(new Date());
simpleTrigger.setRepeatInterval(2000);
simpleTrigger.setRepeatCount(100);
③
通过
SchedulerFactory
获取一个调度器实例
SchedulerFactory schedulerFactory = new
StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.scheduleJob(jobDetail, simpleTrigger);
④
注册并进行调度
scheduler.start();
⑤
调度启动
} catch (Exception e) {
e.printStackTrace();
}
}
}
首先在
①
处通过
JobDetail
封装
SimpleJob
,同时指定
Job
在
Scheduler
中所属组及名称,这里,组名为
jGroup1
,而名称为
job1_1
。
在
②
处创建一个
SimpleTrigger
实例,指定该
Trigger
在
Scheduler
中所属组及名称。接着设置调度的时间规则。
最后,需要创建
Scheduler
实例,并将
JobDetail
和
Trigger
实例注册到
Scheduler
中。这里,我们通过
StdSchedulerFactory
获取一个
Scheduler
实例,并通过
scheduleJob(JobDetail
jobDetail, Trigger trigger)
完成两件事:
1)
将
JobDetail
和
Trigger
注册到
Scheduler
中;
2)
将
Trigger
指派给
JobDetail
,将两者关联起来。
当
Scheduler
启动后,
Trigger
将定期触发并执行
SimpleJob
的
execute(JobExecutionContext jobCtx)
方法,然后每
10
秒重复一次,直到任务被执行
100
次后停止。
还可以通过
SimpleTrigger
的
setStartTime(java.util.Date startTime)
和
setEndTime(java.util.Date endTime)
指定运行的时间范围,当运行次数和时间范围冲突时,超过时间范围的任务运行不被执行。如可以通过
simpleTrigger.setStartTime(new
Date(System.currentTimeMillis() + 60000L))
指定
60
秒钟以后开始。
除了通过
scheduleJob(jobDetail,
simpleTrigger)
建立
Trigger
和
JobDetail
的关联,还有另外一种关联
Trigger
和
JobDetail
的方式:
JobDetail jobDetail = new
JobDetail("job1_1","jGroup1", SimpleJob.class);
SimpleTrigger simpleTrigger = new
SimpleTrigger("trigger1_1","tgroup1");
…
simpleTrigger.setJobGroup("jGroup1");
①
-1
:指定关联的
Job
组名
simpleTrigger.setJobName("job1_1");
①
-2
:指定关联的
Job
名称
scheduler.addJob(jobDetail, true);
②
注册
JobDetail
scheduler.scheduleJob(simpleTrigger);
③
注册指定了关联
JobDetail
的
Trigger
在这种方式中,
Trigger
通过指定
Job
所属组及
Job
名称,然后使用
Scheduler
的
scheduleJob(Trigger
trigger)
方法注册
Trigger
。有两个值得注意的地方:
通过这种方式注册的
Trigger
实例必须已经指定
Job
组和
Job
名称,否则调用注册
Trigger
的方法将抛出异常;
引用的
JobDetail
对象必须已经存在于
Scheduler
中。也即,代码中
①
、
②
和
③
的先后顺序不能互换。
在构造
Trigger
实例时,可以考虑使用
org.quartz.TriggerUtils
工具类,该工具类不但提供了众多获取特定时间的方法,还拥有众多获取常见
Trigger
的方法,如
makeSecondlyTrigger(String trigName)
方法将创建一个每秒执行一次的
Trigger
,而
makeWeeklyTrigger(String
trigName, int dayOfWeek, int hour, int minute)
将创建一个每星期某一特定时间点执行一次的
Trigger
。而
getEvenMinuteDate(Date date)
方法将返回某一时间点一分钟以后的时间。
使用
CronTrigger
CronTrigger
能够提供比
SimpleTrigger
更有具体实际意义的调度方案,调度规则基于
分享到:
相关推荐
Spring整合任务调度框架Quartz,本文档详细介绍了Spring整合任务调度框架Quartz,希望可以帮助学习者
Java任务调度框架Quartz2.0.2(版本:2.0.2)教程实例源代码
Java任务调度框架Quartz1.8.6(版本:1.8.6)教程实例源代码
任务调度框架Quartz[收集].pdf
Quartz框架是一个全功能、开源的任务调度服务,可以集成几乎任何的java应用程序—从小的单片机系统到大型的电子商务系统。Quartz可以执行上千上万的任务调度
NULL 博文链接:https://baobeituping.iteye.com/blog/629237
目前主流的分布式任务调度框架 个人建议用Quartz 文档全资料多 不过看自己项目需求把 TBSchedule和uncode-schedule基于zookeeper 不用数据库 总之每个框架有各自的优缺点,需求自己略做验证。 附件中有项目的下载...
spring quartz 时间任务调度框架 spring quartz 时间任务调度框架 spring quartz 时间任务调度框架
Quartz是OpenSymphony开源组织的一个Java开源项目,在2009被Terracotta收购。Quartz官网 Quartz任务调度的主要元素有: Trigger(触发器) Scheduler(任务调度器) Job(任务) 其中Tri
任务调度Quartz框架 任务调度Quartz框架
Quartz任务调度框架教程中文版 chm格式
任务调度开源框架Quartz动态添加、修改和删除定时任务
集成了分布式任务调度框架 Quartz ,任务存储于数据库。 使用SpringMVC作为路由控制, 集成 Swagger2 提供实时 RESTful API文档。 数据持久层集成 Mybatis 框架。 使用自定义注解 @TargetDataSource 实现了多数据源...
制定Quartz.NET调度计划,以及监控Quartz.NET以及Topshelf运行情况 应用场景:定时Job配置以及定时Job运行情况记录
任务调度本身涉及到多线程并发、运行时间规则制定和解析、场景保持与恢复、线程池维护等诸多方面的工作。如果直接使用自定义线程这种刀耕火种的原始办法,开发任务调度程序是一项颇具挑战性的工作。Java开源的好处...
任务调度框架Quartz,Quartz是一个开源的任务调度框架,能够安排多个任务在不同的时间执行。
Quartz-2.2.1 任务调度框架在Java项目中的使用实例 Demo 在这个小Demo 中使用了Java 类的反射机制,通用的项目实例,高度抽象的实例。 在业务需求不是很复杂的情况下,完全可以减少因为使用Quartz任务调度框架的代码...