前言
任务调度是大多数企业应用常用需求,将一部分任务单独提取出来,交给任务调度线程在特点的时间执行,针对一些对实时性不高,数据量较大的功能,比如:报表数据统计,刷数,同步数据等功能。
任务调动本身涉及到的技术:多线程,线程池,运行时间规则解析,运行现场保持与恢复等,对此开源java框架Quartz专门提供了任务调度的功能。
一、Quartz的抽象
Quartz抽象出三大核心:调度器,触发器,任务。抽象出一下接口:
1)Job
提供execute方法,实现该接口编写业务逻辑,面向程序员的接口。
2)JobDetail
用以描述Job的基本信息,比如Class,名称,描述,是否持久化等,Quartz通过JobDetail提供的信息,使用反射技术创建Job实例,JobDetail面向Quartz。
3)Trigger
描述Job执行的时间规则,最常见的子类:SimpleTrigger和CronTrigger,仅需触发一次或者周期触发可以使用SimpleTrigger,CronTrigger涉及到灵活的Cron表达式。其派生类包含一下重要信息:
startTime:首次触发时间
endTime:截止时间
nextFireTime:下一次触发时间
previousFireTime:上一次触发时间
repeatCount:重复执行次数(不包含首次执行)
repeatInterval:重复执行间隔,单位为毫秒
timesTriggered:总触发次数
4)Scheduler
调度器,代表一个独立运行的Quartz容器,可将JobDetail和Trigger注册到Scheduler中,Scheduler负责扫描需要执行的Job任务。
引入库:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
二、Quartz调度
通过SchedulerFactory创建配置调度器Scheduler,Scheduler主要有两个调度线程:
1)执行常规调度的线程:轮询存储的所有 trigger,如果有需要触发的 trigger,即到达了下一次触发的时间,则从任务执行线程池获取一个空闲线程,执行与该 trigger 关联的任务。
2)执行MisfiredTrigger(错过执行)的线程:扫描所有的 trigger,查看是否有MisfiredTrigger,如果有的话根据misfire策略分别处理。
2.1 Misfire的原因
1.线程池资源不足,job没来得及分配线程执行。
2.scheduler意外停止,导致job没有执行。
3.job使用了@DisallowConcurrentExecution注解,job不能并发执行,当达到下一个job执行点的时候,上一个任务还没有完成。
4.job指定了过去的开始执行时间,例如当前时间是8点00分00秒,指定开始时间为7点00分00秒。
misfire产生需有2个前提:1)job到达触发时间时没有被执行,2)被执行的延迟时间超过了Quartz配置的misfireThreshold阀值。如果延迟执行的时间小于阀值,则Quartz不认为发生了misfire,立即执行job;如果延迟执行的时间大于或者等于阀值,则被判断为misfire,然后按照指定的策略执行。
2.2 Misfire的处理策略(仅CronTrigger规则)
假设8:00的任务执行了,但是由于某些原因,scheduler没有执行9:00和10:00的任务,在10:15分的时候scheduler发现job有两次没有执行,这两次的延迟执行时间分别是1小时15分和15分,都大于设置的misfireThreshold=1秒,因此发生了两次misfire。各misfire策略如下:
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW:立即执行第一个发生misfire的任务,忽略其他发生misfire的任务,然后按照原计划继续执行。例如:在10:15立即执行9:00任务,忽略10:00任务,然后等待下一个任务在11:00执行,后续按照原计划执行。
MISFIRE_INSTRUCTION_SMART_POLICY:默认策略,等同MISFIRE_INSTRUCTION_FIRE_ONCE_NOW。
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY:Quartz不会判断发生了misfire,立即执行所有发生了misfire的任务,然后按照原计划进行执行。例如:10:15分立即执行9:00和10:00的任务,然后等待下一个任务在11:00执行,后续按照原计划执行。
MISFIRE_INSTRUCTION_DO_NOTHING:所有发生misfire的任务都被忽略,只是按照原计划继续执行。