Quartz定时任务调度
1.quartz基本介绍
Quatz 是一个特性丰富的,开源的任务调度库,它几乎可以嵌入所有的 Java 程序,从很小的独立应用程序到大型商业系统。Quartz 可以用来创建成百上千的简单的或者复杂的任务,这些任务可以用来执行任何程序可以做的事情。Quartz 拥有很多企业级的特性,包括支持 JTA 事务和集群。
结构图
2.核心组件
2.1JobDetail
创建一个实现 Job 接口的类,使用 JobBuilder 包装成 JobDetail,它可以携带 KV 的数据
2.2Trigger
2.2.1 SimpleTrigger
SimpleTrigger 可以定义固定时刻或者固定时间间隔的调度规则(精确到毫秒).
例如:每天 9 点钟运行;每隔 30 分钟运行一次
2.2.2 CalendarIntervalTrigger
CalendarIntervalTrigger 可以定义更多时间单位的调度需求,精确到秒。
例如:每年、每个月、每周、每天、每小时、每分钟、每秒
2.2.3 DailyTimeIntervalTrigger
每天的某个时间段内,以一定的时间间隔执行任务。
例如:每天早上 9 点到晚上 9 点,每隔半个小时执行一次,并且只在周一到周六执行。
2.2.4 CronTrigger
CronTirgger 可以定义基于 Cron 表达式的调度规则,是最常用的触发器类型。
- 星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,在分钟字段时,表示“每分钟”;
- 问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
- 减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从 10 到 12 点,即 10,11,12;
- 逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
- 斜杠(/):x/y 表达一个等步长序列,x 为起始值,y 为增量步长值。如在分钟字段中使用 0/15,则表示为 0,15,30 和 45 秒,而 5/15 在分钟字段中表示 5,20,35,50,你也可以使用*/y,它等同于 0/y;
- L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L 在日期字段中,表示 这个月份的最后一天,如一月的 31 号,非闰年二月的 28 号;如果 L 用在星期中,则表示星期六,等同于 7。但是,如果 L 出现在星期字段里,而且在前面有一个数值 X,则表示“这个月的最后 X 天”,例如,6L 表示该月的最后星期五;
- W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如 15W 表示离该月 15号最近的工作日,如果该月 15 号是星期六,则匹配 14 号星期五;如果 15 日是星期日,则匹配 16 号星期一;如果 15号是星期二,那结果就是 15 号星期二。但必须注意关联的匹配日期不能够跨月,如你指定 1W,如果 1 号是星期六,结果匹配的是 3 号星期一,而非上个月最后的那天。W 字符串只能指定单一日期,而不能指定日期范围;
- LW 组合:在日期字段可以组合使用 LW,它的意思是当月的最后一个工作日;
- #号(#):该字符只能在星期字段中使用,表示当月某个工作日。如 6#3 表示当月的第三个星期五(6 表示星期五,#3 表示当前的第三个),而 4#5 表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
- C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如 5C 在日期字段中就相当于日历 5 日以后的第一天。1C 在星期字段中相当于星期日后的第一天。
- Cron 表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。
2.2.5基于Calendar的排除规则
Calendar名称 | 称用法 |
---|---|
BaseCalendar | 为高级的 Calendar 实现了基本的功能,实现了 org.quartz.Calendar 接口 |
AnnualCalendar | 排除年中一天或多天 |
CronCalendar | 日历的这种实现排除了由给定的CronExpression表达的时间集合。 例如,您可以使用此日历使用表达式“* * 0-7,18-23?* *”每天排除所有营业时间(上午8点至下午5点)。 如果CronTrigger具有给定的cron表达式并且与具有相同表达式的CronCalendar相关联,则日历将排除触发器包含的所有时间,并且它们将彼此抵消。 |
DailyCalendar | 您可以使用此日历来排除营业时间(上午8点 - 5点)每天。 每个DailyCalendar仅允许指定单个时间范围,并且该时间范围可能不会跨越每日边界(即,您不能指定从上午8点至凌晨5点的时间范围)。 如果属性invertTimeRange为false(默认),则时间范围定义触发器不允许触发的时间范围。 如果invertTimeRange为true,则时间范围被反转 - 也就是排除在定义的时间范围之外的所有时间。 |
DailyCalendar | 您可以使用此日历来排除营业时间(上午8点 - 5点)每天。 每个DailyCalendar仅允许指定单个时间范围,并且该时间范围可能不会跨越每日边界(即,您不能指定从上午8点至凌晨5点的时间范围)。 如果属性invertTimeRange为false(默认),则时间范围定义触发器不允许触发的时间范围。 如果invertTimeRange为true,则时间范围被反转 - 也就是排除在定义的时间范围之外的所有时间。 |
HolidayCalendar | 特别的用于从 Trigger 中排除节假日 |
MonthlyCalendar | 排除月份中的指定数天,例如,可用于排除每月的最后一天 |
WeeklyCalendar | 排除星期中的任意周几,例如,可用于排除周末,默认周六和周日 |
2.3 Scheduler
- 操作调度器本身,例如调度器的启动start()、调度器的关闭shutdown()。
- 操作Trigger,例如pauseTriggers()、resumeTrigger()。
- 操作Job,例如scheduleJob()、unscheduleJob()、rescheduleJob()
2.4 Listener
Quartz中提供了三种Listener,监听Scheduler的,监听Trigger的,监听Job的。
2.5集群
如何实现任务的不重跑不漏跑.基于数据库实现;
默认会加锁,什么情况下不会上锁呢?JobStoreSupport的executeInNonManagedTXLock()方法
3. springboot集成quartz
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
修改quartz.properties属性配置
#quartz定时任务
#初始化表结构
##集群只在存储方式是jobstore才有效
spring.quartz.job-store-type=jdbc
##同样名字的job在插入到表中会报错,设置为true会覆盖之前相同名字的job
spring.quartz.overwrite-existing-jobs=true
#选择quartz的事务,其通过JobStoreTX来管理
spring.quartz.properties.org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
##配置集群的话,必须实例名一样
spring.quartz.properties.org.quartz.scheduler.instanceName = MyClusteredScheduler
#本地启动时,若不想跑线上的定时可以用这个集群实例。不用修改实例名直接使用
#spring.quartz.properties.org.quartz.scheduler.instanceName = Local_ClusteredScheduler
##根据主机以及时间戳生成实例id
spring.quartz.properties.org.quartz.scheduler.instanceId = AUTO
#SimpleThreadPool是Quartz.Net中自带的线程池,默认个数为10个,代表一个Scheduler同一时刻并发的最多只能执行10个job,超过10个的job需要排队等待。
spring.quartz.properties.org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# threadCount和threadPriority将以setter的形式注入ThreadPool实例
# 并发个数 如果你只有几个工作每天触发几次 那么1个线程就可以,如果你有成千上万的工作,每分钟都有很多工作 那么久需要50-100之间.
# 只有1到100之间的数字是非常实用的
spring.quartz.properties.org.quartz.threadPool.threadCount = 50
##mysql使用的驱动代理
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_
##开启集群配置
spring.quartz.properties.org.quartz.jobStore.isClustered=true
##调整for update强制走主库
spring.quartz.properties.org.quartz.jobStore.selectWithLockSQL=SELECT /*master*/ * FROM {0}LOCKS WHERE SCHED_NAME = {1} AND LOCK_NAME = ? FOR UPDATE
#调用是为了通知SchedulerPlugin它应该释放它的所有资源,该插件捕获 JVM 终止的事件(例如在 CRTL-C 上)并告诉 scheuler 关闭。
spring.quartz.properties.org.quartz.plugin.shutdownHook.class=org.quartz.plugins.management.ShutdownHookPlugin
spring.quartz.properties.org.quartz.plugin.shutdownHook.cleanShutdown=TRUE