赞
踩
为什么需要任务调度框架:
类似基于这种:基于准确的时刻或者类似的时间间隔触发的任务、批量数据处理、要实现两个动作解耦的场景,我们都可以用任务调度来实现。
我们对任务调度框架的基本需求:
任务调度跨框架对比:
| 层次 | 举例 | 特点 |
|---|---|---|
| 操作系统 | 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 是一个老牌的任务调度系统,98 年构思,01 年发布到 sourceforge。现在更新比较慢,因为已经非常成熟了。
官网:http://www.quartz-scheduler.org/
GitHub地址:https://github.com/quartz-scheduler/quartz
Quartz 的目的就是让任务调度更加简单,开发人员只需要关注业务即可。他是用 Java 语言编写的(也有.NET 的版本)。Java 代码能做的任何事情,Quartz 都可以调度。
特点:
依赖:
<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体系结构:

我们创建一个实现 Job 接口的类,使用 JobBuilder 包装成 JobDetail,它可以携带
KV 的数据(通过JobDataMap)用于扩展属性,在运行的时候可以从 context获取到,然后通过name+group唯一确定一个JobDetail。
定时任务的触发规则(触发器),定义了任务调度的触发时机,使用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 表达式的调度规则,是最常用的触发器类型。
调度器,是 Quartz 的指挥官,由 StdSchedulerFactory 产生。它是单例的。并且是 Quartz 中最重要的 API,默认是实现类是 StdScheduler,里面包含了一个QuartzScheduler。QuartzScheduler 里面又包含了一个 QuartzSchedulerThread(调度线程池)。
Scheduler中的方法分为三大类:
1)操作调度器本身,例如调度器的启动 start()、调度器的关闭 shutdown()。
2)操作 Trigger,例如 pauseTriggers()、resumeTrigger()。
3)操作 Job,例如 scheduleJob()、unscheduleJob()、rescheduleJob()
我们有这么一种需求,在每个任务运行结束之后发送通知给运维管理员。那是不是要在每个任务的最后添加一行代码呢?这种方式对原来的代码造成了入侵,不利于维护。如果代码不是写在任务代码的最后一行,怎么知道任务执行完了呢?或者说,怎么监测到任务的生命周期呢?那就需要Listener来监控任务的生命周期。
Quartz 中提供了三种 Listener,监听 Scheduler 的,监听 Trigger 的,监听 Job 的。
只需要创建类实现相应的接口,并在 Scheduler 上注册 Listener,便可实现对核心对象的监听。
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两个触发,合情合理。
如果要在触发器的基础上,排除一些时间区间(比如圣诞节、春节等等)不执行任务,就要用到 Quartz 的Calendar 类(注意不是 JDK 的 Calendar)。可以按年、月、周、日、特定日期、Cron表达式排除。
调用 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(); } }
首先定义一个类实现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(); } }
这里接不写代码了,直接说明生命周期方法。
| 方法 | 描述 |
|---|---|
| getName() | 获取监听器名称 |
| triggerFired() | Trigger 被触发, Job 上的 execute() 方法将要被执行时, Scheduler 就调用这个方法 |
| vetoJobExecution() | 在 Trigger 触 发 后 , Job 将 要 被 执 行 时 由 Scheduler 调 用 这 个 方 法 。TriggerListener 给了一个选择去否决 Job 的执行。 假如这个方法返回 true, 这个 Job 将不会为此次 Trigger 触发而得到执行,就是如果该方法返回true,代表这个任务不会被执行 |
| triggerMisfired() | Trigger 错过触发时调用 |
| triggerComplete() | Trigger 被触发并且完成了 Job 的执行时, Scheduler 调用这个方法 |
| 方法 | 描述 |
|---|---|
| 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方法的回调 |
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 中有两种存储任务的方式,一种在在内存,一种是在数据库。
quartz 默认的 JobStore 是 RAMJobstore,也就是把任务和触发器信息运行的信息存储在内存中,用到了 HashMap、TreeSet、HashSet 等等数据结构。如果程序崩溃或重启,所有存储在内存中的数据都会丢失。所以我们需要把这些数据持久化到磁盘。
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 的信息 |
验证:

执行一次调度后,就把相关信息持久化到数据库的表中。
就是配置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配置类的话,就跟使用原生那样配置就可以了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。