定时任务是日常开发中非常常见的功能.
对于简单的任务处理 Spring 的 @Scheduled 非常好用.
如果处理更复杂的情况, 比如需要宕机恢复或者集群调度, 那么 Quartz 是个不错的轻量级方案.
一些重量级的第三方任务调度系统也是基于 Quartz 扩展的, 比如 XXL-JOB https://github.com/xuxueli/xxl-job
Quartz 的模块
Quartz 模块. PNG
Trigger 定义了何时触发任务, 主要是两种 SimpleTrigger 和 CronTigger, 其他 Tigger 基本都可以通过这两种实现. Trigger 还可以定义错过的任务如何处理. 下表是说明:
trigger.jpeg
Calendar 与 Trigger 相反, Calendar 定义哪些时间是特例, 不能执行任务. Calendar 的优先级高于 Trigger.HolidayCalendar 比较常用, 定义了哪些节日是特殊情况.
calendar.jpeg
Job 负责定义任务所处理的逻辑, 实现类需要实现 org.quartz.Job 接口
- public interface Job {
- void execute(JobExecutionContext context) throws JobExecutionException;
- }
通过抛出 JobExecutionException 可以强制控制任务后续处理
- public class JobExecutionException extends SchedulerException {
- private boolean refire = false;//true: 重新执行任务 (不会触发下一次)
- private boolean unscheduleTrigg = false;//true: 直接标记 Trigger 完成
- private boolean unscheduleAllTriggs = false;//true: 直接标记所有和 Job 相关的 Trigger 都已经完成
- }
Stateful Job. 不同于一般的无状态任务, 可以同时执行. 有状态任务不能同时执行, 而且需要保存状态. 根据需要给可以 Job 类添加 @PersistJobDataAfterExecution 或 @DisallowConcurrentExecution
- @PersistJobDataAfterExecution
- @DisallowConcurrentExecution
- public interface StatefulJob extends Job {
- }
JobDetail 保存 Job 的元信息, 包括类定义和设置.
SchedulerFactory 负责初始化, 读取配置文件, 然后创建 Scheduler
Scheduler 是中枢调度器, 负责管理 Trigger/JobDetail 和 3 个调度线程
QuartzSchedulerThread 主调度线程
调度任务主流程 (1).PNG
MisfireHandler 错失触发的任务恢复线程,. 更新 Trigger 的触发时间.
ClusterManager 集群协调线程. 定期心跳, 自动 recover. 同主程序中的 recover.
SpringBoot2.0 集成 Quartz
从 boot2.0 开始, 增加了对 Quartz 的自动装配, 以前需要自己处理.
以集群配置为例, 最基本的操作是这样的:
Maven 依赖
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-quartz</artifactId>
- </dependency>
编写具体的 job
- public class TestJob extends QuartzJobBean {
- @Override
- protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
- System.out.println("Test job executed.");
- }
- }
定义 JobDetail, 把 Job 的 class 类型传入. Spring 自动处理
- @Bean
- public JobDetail testJob(){
- return JobBuilder
- .newJob(TestJob.class)
- .withIdentity("TestJob")
- .storeDurably()
- .requestRecovery()
- .build();
- }
定义 Trigger, 通过 Key 关联 JobDetail.Spring 自动处理.
- @Bean
- public Trigger testTrigger(){
- return TriggerBuilder.newTrigger()
- .withIdentity("TestTrigger")
- .forJob("TestJob")
- .withSchedule(CronScheduleBuilder
- .cronSchedule("0/6 * * * * ?")
- .withMisfireHandlingInstructionDoNothing())
- .build();
- }
如果需要不同的数据库, 定义一个 @Primary 主库, 和一个 @QuartzDataSource quartz 专用库
- @Bean
- @Primary
- @ConfigurationProperties(prefix="spring.datasource")
- public DataSource primaryDataSource() {
- return DataSourceBuilder.create().build();
- }
- @Bean
- @QuartzDataSource
- @ConfigurationProperties(prefix="spring.datasource.quartz")
- public DataSource quartzDataSource() {
- return DataSourceBuilder.create().build();
- }
application.properties 都有默认配置, 第一行启用数据库, 后面两行是 cluster 功能
- spring.quartz.job-store-type=jdbc
- spring.quartz.org.quartz.scheduler.instanceId = AUTO
- spring.quartz.org.quartz.jobStore.isClustered = true
虽然 Spring 提供了自动建库的功能, 但是第一次建完之后需要改成 never
- spring.quartz.jdbc.initializeSchema=ALWAYS
- #spring.quartz.jdbc.initializeSchema=NEVER
手动建库可以从官网下载的全家桶, 或者 Jar 包中获得
全家桶:{dir}/docs/dbTables/tables_{database}.sql
Jar:classpath:/org/quartz/impl/jdbcjobstore/tables_{database}.sql
想看源码的可以从 org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration 进
来源: http://www.jianshu.com/p/4758f619346d