2019独角兽企业重金招聘Python工程师标准>>>
quartz常用api
- Scheduler 调度程序交互的主要API。
- Job 希望由调度程序执行的组件实现的接口。
- JobDetail 用于定义作业的实例。
- JobDataMap 可以包含不限量的序列化数据,在job运行的时候可以使用
- JobBuilder 用于定义/构建JobDetail实例,用于定义作业的实例。
- Trigger 定义执行给定作业的计划的组件。
- TriggerBuilder 用于定义/构建触发器实例。
- JobListener TriggerListener SchedulerListener监听器 用于对组件的监听
入门小案列
package com;
import org.quartz.*;
import org.quartz.impl.StdScheduler;
import org.quartz.impl.StdSchedulerFactory;
/**
* @program: hello
* @description: 任务调度入口
* @author: lee
* @create: 2019-01-13
**/
public class PScheduller {
public static void main(String[] args) {
try {
//调度器,从工厂中获得调度的实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
JobDetail jobDetail = JobBuilder.newJob(PJob.class)
.withIdentity("jobid", "job分组")//jobid必须唯一
.build();
Trigger trigger=TriggerBuilder.newTrigger()
.withIdentity("triggerid", "trigger分组")
//每两秒重复一次,不断的重复下去
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInMilliseconds(2000).repeatForever())
.build();
//让调度区关联任务和触发器
scheduler.scheduleJob(jobDetail,trigger);
//启动调度器
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
Job任务类
package com;
import com.sun.org.apache.xpath.internal.SourceTree;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @program: hello
* @description: 自定义Job
* @author: lee
* @create: 2019-01-13
**/
public class PJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date date=new Date();
SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString=dateFormat.format(date);
System.out.println("正在运行job任务,运行时间是:"+dateString);
}
}
运行结果
正在运行job任务,运行时间是:2019-01-13 10:47:44
正在运行job任务,运行时间是:2019-01-13 10:47:47
正在运行job任务,运行时间是:2019-01-13 10:47:47
正在运行job任务,运行时间是:2019-01-13 10:47:44
正在运行job任务,运行时间是:2019-01-13 10:47:48
...
Job与JobDetail介绍
程序定义了一个实现Job接口的类,这个类仅仅表明该job需要完成什么类型的任务,除此之外,Quartz还需要知道该Job实例所包含的属性;这将由JobDetail类来完成。
Job:工作任务调度的接口,任务类需要实现该接口,需要在接口中的execute方法中实现自己的业务逻辑。
每次调度器执行Job的时候,它在调用execute方法的时候会创建一个新的Job实例,当调用完成以后,关联的Job对象会被释放,释放的实例会被垃圾回收机制回收。
JobDetail:JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,它用来存储特定实例的状态信息,调度器需要借助JobDetail对象来添加Job实例。
JobDetail的重要属性:name,group,jobclass,jobDataMap;
JobDataMap
JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。
JobDetail jobDetail = JobBuilder.newJob(PJob.class)
.withIdentity("jobid", "job分组")//jobid必须唯一
.usingJobData("nihao","fasdfsad")
.usingJobData("nihao2","fasdfsad2")
.build();
获取传递的数据
public class PJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date date=new Date();
SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString=dateFormat.format(date);
System.out.println("正在运行job任务,运行时间是:"+dateString);
//后续讲到的删除定时任务就需要用到Jobkey
JobKey jobKey=jobExecutionContext.getJobDetail().getKey();
JobDataMap jobDataMap=jobExecutionContext.getJobDetail().getJobDataMap();
String s1=jobDataMap.getString("nihao");
String s2=jobDataMap.getString("nihao2");
System.out.println(s1);
System.out.println(s2);
System.err.println(jobKey);
}
}
Triggers
所有类型的trigger都有TriggerKey这个属性,表示trigger的身份;除此之外,trigger还有很多其它的公共属性。这些属性,在构建trigger的时候可以通过TriggerBuilder设置。
trigger的公共属性有:
-
jobKey属性:当trigger触发时被执行的job的身份;
-
startTime属性:设置trigger第一次触发的时间;该属性的值是java.util.Date类型,表示某个指定的时间点;有些类型的trigger,会在设置的startTime时立即触发,有些类型的trigger,表示其触发是在startTime之后开始生效。比如,现在是1月份,你设置了一个trigger–“在每个月的第5天执行”,然后你将startTime属性设置为4月1号,则该trigger第一次触发会是在几个月以后了(即4月5号)。
-
endTime属性:表示trigger失效的时间点。比如,”每月第5天执行”的trigger,如果其endTime是7月1号,则其最后一次执行时间是6月5号。
优先级(priority)
如果你的trigger很多(或者Quartz线程池的工作线程太少),Quartz可能没有足够的资源同时触发所有的trigger;这种情况下,你可能希望控制哪些trigger优先使用Quartz的工作线程,要达到该目的,可以在trigger上设置priority属性。比如,你有N个trigger需要同时触发,但只有Z个工作线程,优先级最高的Z个trigger会被首先触发。如果没有为trigger设置优先级,trigger使用默认优先级,值为5;priority属性的值可以是任意整数,正数、负数都可以。
Trigger trigger=TriggerBuilder.newTrigger()
.withIdentity("triggerid", "trigger分组")
//每两秒重复一次,不断的重复下去
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInMilliseconds(2000)
.repeatForever())
.withPriority(8)
.build();
注意:只有同时触发的trigger之间才会比较优先级。10:59触发的trigger总是在11:00触发的trigger之前执行。
注意:如果trigger是可恢复的,在恢复后再调度时,优先级与原trigger是一样的。
Trigger
Simple Trigger
代码实例
package com;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.Date;
/**
* @program: hello
* @description: 任务调度入口
* @author: lee
* @create: 2019-01-13
**/
public class PScheduller {
public static void main(String[] args) throws SchedulerException {
// 通过SchedulerFactory获取一个调度器实例
StdSchedulerFactory sf = new StdSchedulerFactory();
// 代表一个Quartz的独立运行容器
Scheduler scheduler = sf.getScheduler();
// 获取当前时间15秒之后的时间
Date startDate = DateBuilder.nextGivenSecondDate(null,15);
// ------------------------------------------Job1 start-------------------------------------------------
// 创建一个JobDetail实例,此版本JobDetail已经作为接口(interface)存在,通过JobBuilder创建
// 并指定Job在Scheduler中所属组及名称
JobDetail jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job1","group1").build();
// SimpleTrigger实现Trigger接口的子接口。此处只指定了开始执行定时任务的时间,使用默认的重复次数(0次)和重复间隔(0秒)
SimpleTrigger simpleTrigger = (SimpleTrigger) TriggerBuilder.newTrigger().withIdentity("trigger1","group1").
startAt(startDate).build();
// 添加JobDetail到Scheduler容器中,并且和Trigger进行关联,返回执行时间
Date date = scheduler.scheduleJob(jobDetail,simpleTrigger);
System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
+ " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
// ------------------------------------------Job1 end-------------------------------------------------
// ------------------------------------------Job2 start-------------------------------------------------
// 与job1相同处理方式的job2,几乎同时执行两个job1和job2任务
jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job2","group1").build();
simpleTrigger = (SimpleTrigger) TriggerBuilder.newTrigger().withIdentity("trigger2","group1").startAt(startDate).build();
date = scheduler.scheduleJob(jobDetail,simpleTrigger);
System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
+ " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
// ------------------------------------------Job2 end-------------------------------------------------
// ------------------------------------------Job3 start-------------------------------------------------
jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job3","group1").build();
// 设置定时任务执行规则为在指定的开始时间进行执行,每个十秒执行一次,重复执行十次。
// 当指定为SimpleScheduleBuilder时,会发现不用对结果进行强制转换为SimpleTrigger了。
simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger3", "group1").startAt(startDate)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).withRepeatCount(10)).build();
System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
+ " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
scheduler.scheduleJob(jobDetail,simpleTrigger);
// forJob 指定trigger对应的定时任务,另外一种实现形式。此Trigger和上面的Trigger共同出发一个任务,执行各自均执行
simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger3","group2").startAt(startDate)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).withRepeatCount(2)).forJob(jobDetail).build();
scheduler.scheduleJob(simpleTrigger);
System.out.println(jobDetail.getKey() + " will [also] run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
+ " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
// ------------------------------------------Job3 end-------------------------------------------------
// ------------------------------------------Job4 start-----------------------------------------------
jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job4","group1").build();
simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger4", "group1").startAt(startDate).
withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).withRepeatCount(5)).build();
date = scheduler.scheduleJob(jobDetail, simpleTrigger);
System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
+ " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
// ------------------------------------------Job4 end-------------------------------------------------
// ------------------------------------------Job5 start-------------------------------------------------
jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job5", "group1").build();
simpleTrigger = (SimpleTrigger)TriggerBuilder.newTrigger().withIdentity("trigger5", "group1")
.startAt(DateBuilder.futureDate(5, DateBuilder.IntervalUnit.MINUTE)).build();
date = scheduler.scheduleJob(jobDetail, simpleTrigger);
System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
+ " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
// ------------------------------------------Job5 end-------------------------------------------------
// ------------------------------------------Job6 start-----------------------------------------------
jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job6", "group1").build();
// 此定时任务的规则为每隔40秒执行一次,永远执行下去
simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger6", "group1").startAt(startDate)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(40).repeatForever()).build();
date = scheduler.scheduleJob(jobDetail, simpleTrigger);
System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount() + " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
// ------------------------------------------Job6 end-------------------------------------------------
System.out.println("------- Starting Scheduler ----------------");
// 在scheduler.start之后调用,可以重新定义trigger,重新注册
scheduler.start();
System.out.println("------- Started Scheduler -----------------");
// ------------------------------------------Job7 start-----------------------------------------------
jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job7", "group1").build();
// 每隔五分钟执行一次,执行20次
simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger7", "group1").startAt(startDate)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInMinutes(5).withRepeatCount(20)).build();
date = scheduler.scheduleJob(jobDetail, simpleTrigger);
System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
+ " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
// ------------------------------------------Job7 end-------------------------------------------------
// ------------------------------------------Job8 start-----------------------------------------------
// storeDurably(),没有触发器指向任务的时候,将任务保存在队列中,然后可以通过手动进行出发
jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job8", "group1").storeDurably().build();
// 任务立即执行
scheduler.addJob(jobDetail, true);
System.out.println("手动触发 triggering job8...");
scheduler.triggerJob(JobKey.jobKey("job8", "group1"));
// ------------------------------------------Job8 end-------------------------------------------------
scheduler.start();
try {
Thread.sleep(60000L);
} catch (InterruptedException e) {
}
// 对Job7进行重新安排
System.out.println("------- Rescheduling... --------------------");
simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger7", "group1").startAt(startDate).
withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInMinutes(5).withRepeatCount(20)).build();
date = scheduler.rescheduleJob(simpleTrigger.getKey(), simpleTrigger);
System.out.println("job7 rescheduled to run at: " + date);
System.out.println("------- Waiting five minutes... ------------");
try {
Thread.sleep(60000L);
} catch (Exception ex) {
}
scheduler.shutdown(true);
SchedulerMetaData metaData = scheduler.getMetaData();
System.out.println("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
}
}
CronTrigger
CronTrigger通常比Simple Trigger更有用,如果您需要基于日历的概念而不是按照SimpleTrigger的精确指定间隔进行重新启动的作业启动计划。
使用CronTrigger,您可以指定号时间表,例如“每周五中午”或“每个工作日和上午9:30”,甚至“每周一至周五上午9:00至10点之间每5分钟”和1月份的星期五“。
即使如此,和SimpleTrigger一样,CronTrigger有一个startTime,它指定何时生效,以及一个(可选的)endTime,用于指定何时停止计划。
CronTrigger配置格式:
格式: [秒] [分] [小时] [日] [月] [周] [年]
说明 | 是否必填 | 允许填写的值 | 允许的通配符 |
---|---|---|---|
秒 | 是 | 0-59 | , - * / |
分 | 是 | 0-59 | , - * / |
小时 | 是 | 0-23 | , - * / |
日 | 是 | 1-31 | , - * ? / L W |
月 | 是 | 1-12 or JAN-DEC | , - * / |
周 | 是 | 1-7 or SUN-SAT | , - * ? / L # |
年 | 否 | empty 或 1970-2099 | , - * / |
* 表示所有值. 例如:在分的字段上设置 *,表示每一分钟都会触发。
? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为? 具体设置为 0 0 0 10 * ?
- 表示区间。例如 在小时上设置 10-12,表示 10,11,12点都会触发。
, 表示指定多个值,例如在周字段上设置 MON,WED,FRI 表示周一,周三和周五触发
/ 用于递增触发。如在秒上面设置5/15 表示从5秒开始,每增15秒触发(5,20,35,50)。 在月字段上设置'1/3'所示每月1号开始,每隔三天触发一次。
L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于7或SAT。如果在L前加上数字,则表示该数据的最后一个。例如在周字段上设置6L这样的格式,则表示本 月最后一个星期五。
-W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上设置15W,表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 1W,它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,W前只能设置具体的数字,不允许区间-).L和W可以一组合使用。如果在日字段上设置LW,则表示在本月的最后一个工作日触发(一般指发工资)
-# 序号(表示每月的第几个周几),例如在周字段上设置6#3表示在每月的第三个周六.注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了)
小提示
周字段的设置,若使用英文字母是不区分大小写的 MON 与mon相同.
常用示例:
0 0 12 * * ? 每天12点触发
0 15 10 ? * * 每天10点15分触发
0 15 10 * * ? 每天10点15分触发
0 15 10 * * ? * 每天10点15分触发
0 15 10 * * ? 2005 2005年每天10点15分触发
0 * 14 * * ? 每天下午的 2点到2点59分每分触发
0 0/5 14 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发)
0 0/5 14,18 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发)
每天下午的 18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 * * ? 每天下午的 2点到2点05分每分触发
0 10,44 14 ? 3 WED 3月分每周三下午的 2点10分和2点44分触发
0 15 10 ? * MON-FRI 从周一到周五每天上午的10点15分触发
0 15 10 15 * ? 每月15号上午10点15分触发
0 15 10 L * ? 每月最后一天的10点15分触发
0 15 10 ? * 6L 每月最后一周的星期五的10点15分触发
0 15 10 ? * 6L 2002-2005 从2002年到2005年每月最后一周的星期五的10点15分触发
0 15 10 ? * 6#3 每月的第三周的星期五开始触发
0 0 12 1/5 * ? 每月的第一个中午开始每隔5天触发一次
0 11 11 11 11 ? 每年的11月11号 11点11分触发(光棍节)
Job Stores
JobStore负责跟踪您提供给调度程序的所有“工作数据”:jobs,triggers,日历等。
RAMJobStore
RAMJobStore是使用最简单的JobStore,它也是性能最高的(在CPU时间方面)。RAMJobStore以其明显的方式获取其名称:它将其所有数据保存在RAM中。这就是为什么它是闪电般快的,也是为什么这么简单的配置。缺点是当您的应用程序结束(或崩溃)时,所有调度信息都将丢失 - 这意味着RAMJobStore无法履行作业和triggers上的“非易失性”设置。对于某些应用程序,这是可以接受的 - 甚至是所需的行为,但对于其他应用程序,这可能是灾难性的。
要使用RAMJobStore(并假设您使用的是StdSchedulerFactory),只需将类名称org.quartz.simpl.RAMJobStore指定为用于配置石英的JobStore类属性:
配置Quartz以使用RAMJobStore
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
JDBC JobStore
JDBCJobStore也被恰当地命名 - 它通过JDBC将其所有数据保存在数据库中。
您需要确定应用程序需要哪种类型的事务。如果您不需要将调度命令(例如添加和删除triggers)绑定到其他事务,那么可以通过使用JobStoreTX作为JobStore 来管理事务(这是最常见的选择)。
如果您需要Quartz与其他事务(即J2EE应用程序服务器)一起工作,那么您应该使用JobStoreCMT - 在这种情况下,Quartz将让应用程序服务器容器管理事务。
配置Quartz以使用JobStoreTx
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
接下来,您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作.StdJDBCDelegate是一个使用“vanilla”JDBC代码(和SQL语句)来执行其工作的委托。
配置JDBCJobStore以使用DriverDelegate
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
接下来,您需要通知JobStore您正在使用的表前缀(如上所述)。
使用表前缀配置JDBCJobStore
org.quartz.jobStore.tablePrefix = QRTZ_
使用要使用的DataSource的名称配置JDBCJobStore
org.quartz.jobStore.dataSource = commonDataSource
TerracottaJobStore
TerracottaJobStore提供了一种缩放和鲁棒性的手段,而不使用数据库。这意味着您的数据库可以免受Quartz的负载,可以将其所有资源保存为应用程序的其余部分。
TerracottaJobStore可以运行群集或非群集,并且在任一情况下,为应用程序重新启动之间持续的作业数据提供存储介质,因为数据存储在Terracotta服务器中。它的性能比通过JDBCJobStore使用数据库要好得多(约一个数量级更好),但比RAMJobStore要慢。
要使用TerracottaJobStore(并且假设您使用的是StdSchedulerFactory),只需将类名称org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore指定为用于配置石英的JobStore类属性,并添加一个额外的行配置来指定Terracotta服务器的位置
配置Quartz以使用TerracottaJobStore
org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore
org.quartz.jobStore.tcConfigUrl = localhost:9510