一、Quartz简介
用过Quartz的都懂,Quartz就是一个完全由java编写的开源作业调度框架。
1、组件简介
需要使用这个框架需要知道几个词。
Job
Job是一个任务接口,开发者定义自己的任务须实现该接口,并重写execute(JobExecutionContext context)方法.Job中的任务有可能并发执行,例如任务的执行时间过长,而每次触发的时间间隔太短,则会导致任务会被并发执行。- 为了避免出现上面的问题,可以在
Job实现类上使用@DisallowConcurrentExecution,保证上一个任务执行完后,再去执行下一个任务
JobDetail
JobDetail是任务详情。- 包含有:任务名称,任务组名称,任务描述、具体任务Job的实现类、参数配置等等信息
- 可以说
JobDetail是任务的定义,而Job是任务的执行逻辑。
Trigger
- Trigger是一个触发器,定义Job执行的时间规则。
- 主要触发器:
SimpleTrigger,CronTrigger,CalendarIntervalTrigger,DailyTimeIntervalTrigger。 SimpleTrigger:从某一个时间开始,以一定的时间间隔来执行任务,重复多少次。CronTrigger: 适合于复杂的任务,使用cron表达式来定义执行规则。CalendarIntervalTrigger:指定从某一个时间开始,以一定的时间间隔执行的任务,时间间隔比SimpleTrigger丰富DailyTimeIntervalTrigger:指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。- 所有的Trigger都包含了
StartTime和endTime这两个属性,用来指定Trigger被触发的时间区间。 - 所有的Trigger都可以设置
MisFire策略. MisFire策略是对于由于系统奔溃或者任务时间过长等原因导致Trigger在应该触发的时间点没有触发.- 并且超过了
misfireThreshold设置的时间(默认是一分钟,没有超过就立即执行)就算misfire(失火)了。 - 这个时候就该设置如何应对这种变化了。激活失败指令(
Misfire Instructions)是触发器的一个重要属性
发生Misfire 对于SimpleTrigger的处理策略
1 | //将任务马上执行一次。对于不会重复执行的任务,这是默认的处理策略。 |
发生Misfire 对于其他的Trigger
1 | //立刻执行一次,然后就按照正常的计划执行。 |
Scheduler
- 调度器,主要是用来管理
Trigger、JobDetail的。 Scheduler可以通过组名或者名称来对Trigger和JobDetail来进行管理- 一个
Trigger只能对应一个Job,但是一个Job可以对应多个Trigger. Scheduler有两个实现类:RemoteScheduler、StdScheduler。但它是由SchdulerFactory创建的。SchdulerFactory是个接口,它有两个实现类:StdSchedulerFactory、DirectSchedulerFactory
2、相关Builder介绍
Quartz提供了相应的Builder方便我们进行构造。
JobBuilder
这个主要方便我们构建任务详情,常用方法:
withIdentity(String name, String group):配置Job名称与组名withDescription(String jobDescription): 任务描述requestRecovery(): 出现故障是否重新执行,默认falsestoreDurably(): 作业完成后是否保留存储,默认falseusingJobData(String dataKey, String value): 配置单个参数keyusingJobData(JobDataMap newJobDataMap): 配置多个参数,放入一个mapsetJobData(JobDataMap newJobDataMap): 和上面类似,但是这个参数直接指向newJobDataMap,直接设置的参数无效
TriggerBuilder
这个主要方便我们构建触发器,常用方法:
withIdentity(String name, String group): 配置Trigger名称与组名withIdentity(TriggerKey triggerKey): 配置Trigger名称与组名withDescription(String triggerDescription): 描述withPriority(int triggerPriority): 设置优先级,默认是:5startAt(Date triggerStartTime): 设置开始时间startNow(): 触发器立即生效endAt(Date triggerEndTime): 设置结束时间withSchedule(ScheduleBuilder<SBT> schedBuilder): 设置调度builder,下面的builder就是
SimpleScheduleBuilder
几种触发器类型之一,最简单常用的。常用方法:
repeatForever():指定触发器将无限期重复withRepeatCount(int triggerRepeatCount):指定重复次数,总触发的次数=triggerRepeatCount+1repeatSecondlyForever(int seconds):每隔seconds秒无限期重复repeatMinutelyForever(int minutes):每隔minutes分钟无限期重复repeatHourlyForever(int hours):每隔hours小时无限期重复repeatSecondlyForever():每隔1秒无限期重复repeatMinutelyForever():每隔1分钟无限期重复repeatHourlyForever():每隔1小时无限期重复withIntervalInSeconds(int intervalInSeconds):每隔intervalInSeconds秒执行withIntervalInMinutes(int intervalInMinutes):每隔intervalInMinutes分钟执行withIntervalInHours(int intervalInHours):每隔intervalInHours小时执行withMisfireHandlingInstructionFireNow():失火后的策略为:MISFIRE_INSTRUCTION_FIRE_NOW
CronScheduleBuilder
算是非常常用的了,crontab 表达式,常用方法:
cronSchedule(String cronExpression):使用cron表达式
简单的一笔
CalendarIntervalScheduleBuilder
常用方法:
inTimeZone(TimeZone timezone):设置时区withInterval(int timeInterval, IntervalUnit unit):相隔多少时间执行,单位有:毫秒、秒、分、时、天、周、月、年withIntervalInSeconds(int intervalInSeconds):相隔秒withIntervalInWeeks(int intervalInWeeks):相隔周withIntervalInMonths(int intervalInMonths):相隔月
等等方法
DailyTimeIntervalScheduleBuilder
withInterval(int timeInterval, IntervalUnit unit):相隔多少时间执行,单位有:秒、分、时,其他单位的不支持会报错withIntervalInSeconds(int intervalInSeconds):相隔秒withIntervalInMinutes(int intervalInMinutes):相隔分withIntervalInHours(int intervalInHours):相隔时onDaysOfTheWeek(Set<Integer> onDaysOfWeek):将触发器设置为在一周的指定日期触发。取值范围可以是1-7,1是星期天,2是星期一…onDaysOfTheWeek(Integer ... onDaysOfWeek):和上面一样,3是星期二…7是星期六onMondayThroughFriday():每星期的周一导周五触发onSaturdayAndSunday():每星期的周六周日触发onEveryDay():每天触发withRepeatCount(int repeatCount):重复次数,总的重复次数=1 (at start time) + repeatCountstartingDailyAt(TimeOfDay timeOfDay):触发的开始时间endingDailyAt(TimeOfDay timeOfDay):触发的结束时间
3、基本使用
引用jar,就可以玩了。
引用依赖
1 | <dependency> |
在pom.xml文件中,引入
简单例子:
1 | public class Demo { |
是不是感觉很简单,自己定义一个类,然后实现Job接口,重写execute(JobExecutionContext context) 方法,然后使用调度器去调度一下即可。
简单玩完了,和springboot整合一波。
二、与Springboot整合
这个才是重点,Springboot基本是一个Java程序猿必备的技能了。什么框架都得和它整一下。
1、引入依赖
1 | <dependency> |
spring官方自己都帮我们搞好了一些配置。
2、配置application.yml
如下:
1 | spring: |
- 这里打算搞个集群版的,就算你部署多台服务器,定时任务会自己负载均衡,不会每台服务器都执行。
- 表的生成,其实可以修改配置,启动的时候自己在数据库生成表。操作方法:
- 修改:
spring.quartz.jdbc.initialize-schema: ALWAYS、说明一下 - 这里有3个值可选:
ALWAYS(每次都生成)、EMBEDDED(仅初始化嵌入式数据源)、NEVER(不初始化数据源)。 - 表生成之后,再改为never即可。注意一点就是我测试了下,发现只有使用
druid数据库连接池才会自动生成表
3、表的说明
会自动生成的表如下:
1 | //以Blob 类型存储的触发器。 |
重要表字段解析
1 | CREATE TABLE `qrtz_job_details` ( |
4、怎么使用
- 当我们引入
spring-boot-starter-quartz的依赖后,springboot在启动的时候会自动加载配置类:org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration。 - 会帮我们初始化好调度器,源码:
SchedulerFactoryBean实现InitializingBean,重写afterPropertiesSet()里面就有了。 - 而且Scheduler,Spring默认是帮我们启动的,不需要手动启动。
- 使用如下:
1
2@Autowired
private Scheduler scheduler;
spring会自己注入,通过工厂类SchedulerFactoryBean的 getObject()方法。
搞个基本的增删改查,很简单,代码也没怎么优化,就这样吧。
1 | @Component |
- 和上面得基本demo没什么两样,就是scheduler 并不需要我们去创建了。
- 当部署多个服务时,也不会重复执行。且任务会负载均衡分配。
5、时间演练
Quartz 提供了下一次运行的时间,我们可以通过下一次运行的时间,比对是否符合我们的预期
1 | public class Test { |
每个 Tigger 都有一个Date getFireTimeAfter(Date afterTime) 的方法,返回的就是下一次运行的时间。