Quartz 开发随笔

控制 Quartz 只执行一次

  • 纯 API 调用的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ScheduleTest {

private static final String JOB_NAME = "job";
private static final String JOB_GROUP = "jobGroup";

public void runOnceTime() throws Exception {
Scheduler scheduler = schedulerFactory.getScheduler();
JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(JOB_NAME, JOB_GROUP).build();
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(0).withRepeatCount(0);
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(JOB_NAME, JOB_GROUP).withSchedule(scheduleBuilder).build();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
  • Spring 的 XML 配置
1
2
3
4
5
6
7
<!-- 配置Spring项目启动后任务就执行一次 -->
<bean id="rsh_simpleTrigger1" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="myJobDetail" />
<property name="startDelay" value="500" />
<property name="repeatInterval" value="0" />
<property name="repeatCount" value="0" />
</bean>

Quartz 控制线程级别的暂停、恢复、停止

Quartz 提供了暂停调度(pauseTrigger、pauseJob)、恢复调度(resumeTrigger、resumeJob)、删除调度(deleteJob)等 API,但本质都是控制 Trigger 的触发,而非直接控制任务线程的运行。如果要进行线程级别的停止,可以调用 scheduler.interrupt() 方法来实现,此时 Job 类需要实现 InterruptableJob 接口。

Quartz 的 Misfire 策略

Quartz 中有一种 Job 接口是 StatefulJob(有状态任务),简单来说这种 Job 使用在不能并发执行的场景下。比如定义了某个任务,每分钟执行一次,对一些数据进行增删操作,正常执行时,每分钟执行的 Job 互不影响;但某个时刻出现了意外,某个任务执行了三分钟,那么当在这个任务持续执行的过程中,不希望并发执行另外两次定时任务的,这就需要使用有状态任务,而没有执行的两次定时任务就是 Misfired Job。还有一种使用场景就是当执行暂停调度(pauseTrigger、pauseJob),然后执行恢复调度 (resumeTrigger、resumeJob),Quartz 会默认在恢复的时候把暂停期间没执行的任务弥补执行。对于判定是否为 Misfired Job,其实有很多条件,目前了解到的有:

  • 调度暂停执行期间,预定的任务未执行
  • 到执行时间时,上一个任务还未完成
  • 过期时间已超过设置的 misfireThreshold 参数值
  • 线程池中已没有空闲线程
  • 线程池中虽有空闲线程,但有优先级更高的任务

产生 Misfire 后,Quartz 会根据 Misfire 策略进行任务的处理,其中 Trigger、CronTrigger、SimpleTrigger 的 Misfire 策略如下:

quartz-trigger-misfire

quartz-crontrigger-misfire

  • MISFIRE_INSTRUCTION_SMART_POLICY,默认的策略
  • MISFIRE_INSTRUCTION_FIRE_ONCE_NOW,立即触发一次,触发后恢复正常的频率
  • MISFIRE_INSTRUCTION_DO_NOTHING,什么都不做,继续等下一次预定时间再触发

quartz-simpletrigger-misfire

在 Spring 的 XML 配置中,Misfire 策略可以在 CronTriggerBean 中配置,同时需要在 quartz.properties 配置文件中指定 misfireThreshold 参数的值(单位为毫秒)。其中 misfireThreshold 表示实际执行时间与下一次应该执行时间之间的差值,超过这个差值就不会执行,低于这个差值就会执行。例如任务每 3 秒执行一次,配置 misfireThreshold=6000,当暂停低于 6 秒内,Quartz 会弥补执行,超过 6 秒就不再弥补执行。具体的配置如下:

1
2
3
4
5
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="buildTask" />
<property name="cronExpression" value="0 30 16 ? * 6" />
<property name="misfireInstruction" value="2"></property>
</bean>
1
org.quartz.jobStore.misfireThreshold = 6000

Quartz Cron 表达式在线生成工具

参考资料