Quartz是什么
Quartz是一个开源的作业调度包,能够运行在几乎任何java项目中,小到单机应用,大到电商系统。Quartz能够创建很容易的调度,也可以创建十个、百个、千个、甚至万个任务的复杂调度。Quartz将任务定义成java组件,能够执行几乎任何你定义的事情。Quartz也支持许多企业级特性,比如JTA和集群。
Quartz的核心是Scheduler、Job、Trigger。Job负责定义需要执行的任务,Trigger负责调度策略,Scheduler负责将二者组合。
Quartz官网:http://www.quartz-scheduler.org/
quartz核心包quartz-2.2.1.jar
<pre>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
</pre>
如果想使用quartz内置的job还需要导入quartz-jobs
<pre>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
</pre>
简单Demo
调度的任务,需要实现Job接口,实现execute()方法。execute方法就是需要执行的任务。
<pre>
public class MyJob implements Job {
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("Hello World,Hello Quartz!");
}
}
</pre>
调度流程
1、通过工厂方法创建调度器(quartz scheduler生命周期的开始)
2、启动调度器
3、定义作业
4、创建触发器
5、组合任务和触发器
6、调度结束(quartz scheduler生命周期结束)
<pre>
public class HelloQuartz {
public static void main(String[] args){
try {
//通过工厂方法创建调度器,整个程序不会停止,直到调用scheduler.shutdown()
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//调度启动
scheduler.start();
//定义作业,并且绑定我们指定的作业类
JobDetail job = newJob(MyJob.class).withIdentity("myJob","group1").build();
//创建触发器
Trigger trigger = newTrigger().withIdentity("myTrigger","group1").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(20).repeatForever()).build();
//调度器添加触发器和作业,开始调度
scheduler.scheduleJob(job,trigger);
//调度关闭
// scheduler.shutdown();
}catch (SchedulerException e){
e.printStackTrace();
}
}
}
</pre>
Quartz的API设计使用的是 DSL(domain specific language),所以非常干净。
如果你直接运行上面的代码应该会抛出:
<pre>
org.quartz.SchedulerConfigException: Thread count must be > 0
at org.quartz.simpl.SimpleThreadPool.initialize(SimpleThreadPool.java:242)
at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1288)
at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1519)
at org.quartz.impl.StdSchedulerFactory.getDefaultScheduler(StdSchedulerFactory.java:1535)
at com.yjz.quartz.HelloQuartz.main(HelloQuartz.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
</pre>
提示线程池中初始化的线程要大于0,那么这个线程池数量在哪里配置?原来Quartz使用一个quartz.properties的配置文件,你可以在你项目中classpath创建该文件(web项目在resources下)。quartz的配置文件还是比较好配置的,可以现简单的配置几项:
<pre>
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
</pre>
- org.quartz.scheduler.instanceName配置的是调度器的名称。
- org.quartz.threadPool.threadCount配置的是线程池中的线程个数
- org.quartz.jobStrore.class配置所有Quartz数据的存储位置,这个是存储在内存中。
更多的配置可以查看:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/configuration/
quartz核心接口
Scheduler:与调度器交互的主要API
Job:任务组件实现的接口
JobDetail:用来定义任务的实例
JobBuilder:用于定义JobDetail实例,定义作业实例
Trigger:为任务定义执行计划的组件
TriggerBuilder:用来定义Trigger实例
Scheduler只有调用了start()方法之后才能触发任务执行,quartz的整个生命周期从通过SchedulerFactory创建Scheduler实例到调用shutdown()方法。
Jobs
之前说过每个job要实现Job接口,job接口里面中定义了指定:
<pre>
public interface Job {
void execute(JobExecutionContext context)
throws JobExecutionException;
}
</pre>
当任务触发后,调度器线程会调用excute()方法,JobExecutionContext提供了job运行时的信息,Scheduler、Trigger、JobDetail等都执行它。每次调度器执行job,都会新建job实例,每次job执行完成后,这个实例会被垃圾回收。还有就是这个job必须有一个无参的构造函数(调度器内部要使用)。
JobDetail:客户端(我们程序)创建JobDetail,并且添加到调度器中,JobDetail包含了job的设置信息。
JobDataMap是JobDetail的一部分,它可以包含任何数量的数据对象,可以用来显示执行过程。JobDataMap实现java Map接口,提供了一些原始的存储和获取。
向JobDataMap中添加数据:
<pre>
JobDetail job = newJob(MyJob.class).withIdentity("myJob","group1").usingJobData("key1","value1").usingJobData("key2",2).build();
</pre>
获取JobMapData:
<pre>
public class MyJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String k1 = dataMap.getString("key1");
int k2 = dataMap.getInt("key2");
System.out.println("Hello World,Hello Quartz!");
}
}
</pre>
JobMapData可以在job中添加一些值,比如:
<pre>
public class DumbJob implements Job {
public DumbJob() {
}
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getMergedJobDataMap(); // Note the difference from the previous example
String jobSays = dataMap.getString("jobSays");
float myFloatValue = dataMap.getFloat("myFloatValue");
ArrayList state = (ArrayList)dataMap.get("myStateData");
state.add(new Date());
System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
}
</pre>
通过context.getMergedJobDataMap()获取,然后回去对应的state值,然后向state中添加值。或者通过注射,在job类中定义对应的setXxx()实现:
<pre>
public class DumbJob implements Job {
String jobSays;
float myFloatValue;
ArrayList state;
public DumbJob() { }
public void execute(JobExecutionContext context)throws JobExecutionException{
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getMergedJobDataMap(); // Note the difference from the previous example
state.add(new Date());
System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
public void setJobSays(String jobSays) {
this.jobSays = jobSays;
}
public void setMyFloatValue(float myFloatValue) {
myFloatValue = myFloatValue;
}
public void setState(ArrayList state) {
state = state;
}
}
</pre>
Trigger
trigger对象用来触发任务执行。当需要调度一个任务时候,需要实例化一个trigger,并且调整参数来给调度器使用。所有的Trigger类型都有TriggerKey属性,用来追踪确定它们的身份。还有一些其它通用参数通过TriggerBuilder构建trigger过程中指定的。
jobKey:用来表明当trigger触发执行时要执行的任务。
startTime:用来表明触发器什么时候开始触发执行。这个值是java.util.Date对象定义的时间。
endTime:用来表明调度触发器什么时候失效。
trigger有多种触发类型,比较常用的有SimpleTrigger和CronTrigger:
SimpleTrigger
当一个任务在一个特定时刻执行一次,或在特定时刻时刻重复执行,可以使用SimpleTrigger。例如你想2017年1月10日 12:32:24执行,或者每十秒执行五次。SimpleTrigger中的开始时间(startTime)、结束时间(endTime)、重复次数(repeat)、重复间隔(repeat interval)。重复可以为0或者一个整数,还可以为SimpleTrigger.REPEAT_INDEFINITELY;重复间隔必须是0或者一个long型,代表毫秒数,如果设置为0将会造成重复触发器同时执行。
CronTrigger
如果你需要基于日历的调度,可以使用CronTirgger,CronTrigger比SimpleTrigger更常用。例如每个月的星期五、每个工作日的九点、每周一九点到十点的每五分钟。
CronTrigger使用Cron-Expressions配置,Cron-Expressions使用以下七种表达式(Sub-Expression)表示:
1、Seconds
2、Minutes
3、Hours
4、Day-of-Month
5、Month
6、Day-of-Week
7、Year
例如:”0 0 12 ? * WED”代表每周三的12:00:00pm
每个二级表达式可以是一个列表,例如:”MON,WED,FRI”
表达式介绍:
- 通配符“xxx”代表的是“每次xxxx时候”,“MON-WED,SAT”每周一到周三和周六。
- ”*”:代表0次或者多次,在Day-Of-Week中代表着”一周中的每一天”。每个字符都需要有效,比如分钟需要在059,小时需要是023,Day-Of-Month必须是131(注意月份的天数),月份是011或者使用:JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC,Day-Of-Week是1~7(1表示周日)或者是SUN, MON, TUE, WED, THU, FRI and SAT。
- ”/“:代表值的增量,例如0/15在分钟里(还可以用0,15,30,45) ,代表的是从0,每小时的15分钟。/35在分钟表达式中代表是每小时的第35分钟,从0开始(还可以表示为0,35)。
- “?”:用在day-of-month和day-of-week里面,用来表示不确定值。
- “L”:用在day-of-month和day-of-week里面,在day-of-month代表每个月的最后一天比如1月的31号和4四月的30号。在day-of-week中代表每周最后一天7(周六)。也可以用在day-of-week后一个字段值,意识是每个月的最后第几天,例如6L,每个月的最后一个星期五,也可以指定偏移量L-3代表每个月的最后第三天。L不能使用指定的列表中,否则会引起混乱。
- “W”:代表的意思是工作日,例如15W,这个月的第15个工作日。
- “#”:代表每个月xxx工作日,例如在day-of-week中6#3,每个月的第三个周五。
例子:
“0 0/5 * * *?” 每五分钟
“10 0/5 * * *?”每五分钟10秒之后
“0 30 10-13 ? * WED,FRI”代表周三周五的10:30,11:30,12:30,13:30
“0 0/30 8-9 5,20 * ?”代表的是每个月5号和20的8点到9点的每半个小时。
代码中使用:
<pre>
Trigger trigger1 = newTrigger().withIdentity("myTrigger","group1").withSchedule(cronSchedule("0 0/2 8-17 * * ?")).forJob("myJob","group1").build();
</pre>
Quartz将任务和触发分离,这样有很多好处,比如:任务创建和存储与触发器相互独立;许多触发器可以触发同一个任务;还可以任务不变的情况下修改和重新定义触发器,这样有效的对调度器进行了解耦合。
Jobs和Trigger还定义了key,注册到调度器中,这样允许将这些key放到不同的分组中,这个功能很实用于一个公司内多个部门,每个部门使用自己的组,这个key是名称和分组的组合,并且这个key唯一。
ScheduleBuilder有多种类型
Quartz的一些主要特性就是这些,当然还有配置和API可以以后再说。
网友评论