具体情况:
Sprinboot-1.x + Quartz-2.x + Java 1.8,设定了Cron表达式为“0 0/3 10-22 * * ?”,即每隔3分钟运行一次任务。
实际运行时,会偶尔有时间点不执行,比如:
10:30, 10:33, 10:36,10:42,10:51,11:00,11:03,11:06,11:15,11:21,11:24.......
执行的任务本身只需要 10 秒左右就能执行完的,因此不存在任务执行时间过长问题。
Quartz也没打印警告或异常信息。
尝试过如下方式无效果:
1. 清空数据库现存的计划任务数据,重新设定计划任务;
2. 修改 org.quartz.jobStore.misfireThreshold = 60000 或 1800000 或 3600000 或 0;
3. 将间隔由 每隔3分钟 改为每隔 15 分钟,那么基本每次都执行,但总时间一长还是会发生跳过的情况;
后台经过同事直接调试源码,得出如下结论:
Quartz 会在你的定时任务执行前后,都进行加锁操作,这些操作有时耗时会过长,而这些时间是算在定时任务里面的。
最终修改的配置参数为下列三个:
# 在调度程序处于空闲状态时,调度程序将在重新查询可用触发器之前等待的时间量(以毫秒为单位),默认是30秒
org.quartz.scheduler.idleWaitTime=300000
# 这个属性???(默认 60000)
org.quartz.jobStore.misfireThreshold = 10000
# 当 org.quartz.scheduler.batchTriggerAcquisitionMaxCount >1
# 并且 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 下面参数 必须设为 true
org.quartz.jobStore.acquireTriggersWithinLock=true
已修复的示例配置:
# 固定前缀org.quartz
# 主要分为scheduler、threadPool、jobStore、plugin等部分
#
# 设置调度器的实例名(instanceName) 和实例ID (instanceId)
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
#如果使用集群,instanceId必须唯一,设置成AUTO
#org.quartz.scheduler.instanceId = AUTO
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
# 在调度程序处于空闲状态时,调度程序将在重新查询可用触发器之前等待的时间量(以毫秒为单位),默认是30秒
org.quartz.scheduler.idleWaitTime=300000
# 允许调度程序节点一次获取(用于触发)的触发器的最大数量,默认是1
org.quartz.scheduler.batchTriggerAcquisitionMaxCount=1
# 允许触发器在其预定的火灾时间之前被获取和触发的时间(毫秒)的时间量,默认是0
org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow=0
# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# 并发个数
org.quartz.threadPool.threadCount = 10
# 优先级(最大值10,最小值1,常用值5)
org.quartz.threadPool.threadPriority = 5
# 加载任务代码的ClassLoader是否从外部继承
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
# 这个属性???(默认 60000)
org.quartz.jobStore.misfireThreshold = 10000
# 当 org.quartz.scheduler.batchTriggerAcquisitionMaxCount >1
# 并且 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 下面参数 必须设为 true
org.quartz.jobStore.acquireTriggersWithinLock=true
# 默认存储在内存中
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
# 持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = myDS
org.quartz.dataSource.myDS.connectionProvider.class:com.hsbc.gbm.hss.chinacustody.filemonitoring.scheduler.DruidForQuartzConfiguration
Springboot1.x配置Quartz的后台代码:
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/* @formatter:off
* @DisallowConcurrentExecution
* 禁止并发执行多个相同定义的JobDetail, 注解是加在Job类上的,
* 但意思并不是不能同时执行多个Job, 而是不能并发执行同一个Job Definition(由JobDetail定义),
* 但是可以同时执行多个不同的J