spring-boot-2.0.3之quartz集成,不是你想的那样哦!
开心一刻
晚上回家,爸妈正在吵架,见我回来就都不说话了
看见我妈坐在那里瞪着我爸,我就问老爸:“你干什么了惹我妈生这么大气?”
我爸说:“没有什么啊,倒是你,这么大了还没有媳妇,要是你有媳妇给我们生一个孙子玩,我们至于吵架吗?”
我一听就感觉要坏,老爸你这是来了一招调虎离山啊,实力坑儿子啊
果然我妈改瞪我了,然后完全不理我爸,直接指着我开骂了……
java 定时任务调度的实现方式
Timer
这个相信大家都有用过,我也用过,但用的不多;
特点是:简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务;能实现简单的定时任务,稍微复杂点(或要求高一些)的定时任务却不好实现。
ScheduledExecutor
这个我相信大家也都用过,而且用的比 Timer 多;正是鉴于 Timer 的缺陷,Java 5 推出了基于线程池设计的 ScheduledExecutor;
特点:每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。需要注意的是,只有当任务的执行时间到来时,ScheduedExecutor 才会真正启动一个线程,其余时间 ScheduledExecutor 都是在轮询任务的状态。
虽然用 ScheduledExecutor 和 Calendar 能够实现复杂任务调度,但实现起来还是比较麻烦,对开发还是不够友善。
Spring Scheduler
spring 对任务调度的实现支持,可以指定任务的执行时间,但对任务队列和线程池的管控较弱;一般集成于项目中,小任务很方便。
JCronTab
JCronTab 则是一款完全按照 crontab 语法编写的 java 任务调度工具。
特点:
可指定任务的执行时间;
提供完全按照 Unix 的 UNIX-POSIX crontab 的格式来规定时间;
支持多种任务调度的持久化方法,包括普通文件、数据库以及 XML 文件进行持久化;
JCronTab 内置了发邮件功能,可以将任务执行结果方便地发送给需要被通知的人;
设计和部署是高性能并可扩展。
Quartz
本文主角,请往下看
当然还有 XXL-JOB、Elastic-Job、Saturn、dolphinscheduler 等等
quartz 相关概念
Scheduler:调度器,进行任务调度;quartz 的大脑
Job:业务 job,亦可称业务组件;定时任务的具体执行业务需要实现此接口,调度器会调用此接口的 execute 方法完成我们的定时业务
JobDetail:用来定义业务 Job 的实例,我们可以称之为 quartz job,很多时候我们谈到的 job 指的是 JobDetail
Trigger:触发器,用来定义一个指定的 Job 何时被执行
JobBuilder:Job 构建器,用来定义或创建 JobDetail 的实例;JobDetail 限定了只能是 Job 的实例
TriggerBuilder:触发器构建器,用来定义或创建触发器的实例
具体为什么要分这么细,大家可以去查阅下相关资料,你会发现很多东西
工程实现
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion><groupId>com.lee</groupId> <artifactId>spring-boot-quartz</artifactId> <version>1.0-SNAPSHOT</version> <properties> <java.version>1.8</java.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <druid.version>1.1.10</druid.version> <pagehelper.version>1.2.5</pagehelper.version> <druid.version>1.1.10</druid.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>${pagehelper.version}</version> </dependency> <!-- 日志 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <exclusions> <!-- 排除spring-boot-starter-logging中的全部依赖 --> <exclusion> <groupId>*</groupId> <artifactId>*</artifactId> </exclusion> </exclusions> <scope>test</scope> <!-- 打包的时候不打spring-boot-starter-logging.jar --> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional><span style="color: rgba(0, 0, 255, 1)">true</span></optional> </dependency> </dependencies> <build> <finalName>spring-boot-quartz</finalName> <plugins> <!-- 打包项目 mvn clean <span style="color: rgba(0, 0, 255, 1)">package</span> --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
application.xml
server: port: 9001 servlet: context-path: /quartz spring: thymeleaf: mode: HTML cache: false #连接池配置 datasource: type: com.alibaba.druid.pool.DruidDataSource druid: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/spring-boot-quartz?useSSL=false&useUnicode=true username: root password: 123456 initial-size: 1 #连接池初始大小 max-active: 20 #连接池中最大的活跃连接数 min-idle: 1 #连接池中最小的活跃连接数 max-wait: 60000 #配置获取连接等待超时的时间 pool-prepared-statements: true #打开 PSCache,并且指定每个连接上 PSCache 的大小 max-pool-prepared-statement-per-connection-size: 20 validation-query: SELECT 1 FROM DUAL validation-query-timeout: 30000 test-on-borrow: false #是否在获得连接后检测其可用性 test-on-return: false #是否在连接放回连接池后检测其可用性 test-while-idle: true #是否在连接空闲一段时间后检测其可用性 quartz: #相关属性配置 properties: org: quartz: scheduler: instanceName: quartzScheduler instanceId: AUTO jobStore: class: org.quartz.impl.jdbcjobstore.JobStoreTX driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate tablePrefix: QRTZ_ isClustered: false clusterCheckinInterval: 10000 useProperties: false threadPool: class: org.quartz.simpl.SimpleThreadPool threadCount: 10 threadPriority: 5 threadsInheritContextClassLoaderOfInitializingThread: true #数据库方式 job-store-type: JDBC #初始化表结构 jdbc: initialize-schema: NEVER #mybatis 配置 mybatis: type-aliases-package: com.lee.quartz.entity mapper-locations: classpath:mybatis/mapper/*.xml #分页配置, pageHelper 是物理分页插件 pagehelper: #4.0.0 以后版本可以不设置该参数,该示例中是 5.1.4 helper-dialect: mysql #启用合理化,如果 pageNum<1 会查询第一页,如果 pageNum>pages 会查询最后一页 reasonable: true logging: level: com.lee.quartz.mapper: debug
这样,quartz 就配置好了,应用里面直接用即可
JobController.java
package com.lee.quartz.web;import com.github.pagehelper.PageInfo;
import com.lee.quartz.common.Result;
import com.lee.quartz.entity.QuartzJob;
import com.lee.quartz.service.IJobService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/job")
public class JobController {
private final static Logger LOGGER = LoggerFactory.getLogger(JobController.class);@Autowired </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> IJobService jobService; @SuppressWarnings({ </span>"unchecked", "rawtypes"<span style="color: rgba(0, 0, 0, 1)"> }) @PostMapping(</span>"/add"<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result save(QuartzJob quartz){ LOGGER.info(</span>"新增任务"<span style="color: rgba(0, 0, 0, 1)">); Result result </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.saveJob(quartz); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result; } @PostMapping(</span>"/list"<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> PageInfo list(String jobName,Integer pageNo,Integer pageSize){ LOGGER.info(</span>"任务列表"<span style="color: rgba(0, 0, 0, 1)">); PageInfo pageInfo </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.listQuartzJob(jobName, pageNo, pageSize); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pageInfo; } @PostMapping(</span>"/trigger"<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result trigger(String jobName, String jobGroup) { LOGGER.info(</span>"触发任务"<span style="color: rgba(0, 0, 0, 1)">); Result result </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.triggerJob(jobName, jobGroup); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result; } @PostMapping(</span>"/pause"<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result pause(String jobName, String jobGroup) { LOGGER.info(</span>"停止任务"<span style="color: rgba(0, 0, 0, 1)">); Result result </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.pauseJob(jobName, jobGroup); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result; } @PostMapping(</span>"/resume"<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result resume(String jobName, String jobGroup) { LOGGER.info(</span>"恢复任务"<span style="color: rgba(0, 0, 0, 1)">); Result result </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.resumeJob(jobName, jobGroup); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result; } @PostMapping(</span>"/remove"<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result remove(String jobName, String jobGroup) { LOGGER.info(</span>"移除任务"<span style="color: rgba(0, 0, 0, 1)">); Result result </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.removeJob(jobName, jobGroup); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result; }
}
JobServiceImpl.java
package com.lee.quartz.service.impl;import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lee.quartz.common.Result;
import com.lee.quartz.entity.QuartzJob;
import com.lee.quartz.mapper.JobMapper;
import com.lee.quartz.service.IJobService;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;
@Service
public class JobServiceImpl implements IJobService {@Autowired </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> Scheduler scheduler; @Autowired </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> JobMapper jobMapper; @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> PageInfo listQuartzJob(String jobName, Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize); List</span><QuartzJob> jobList =<span style="color: rgba(0, 0, 0, 1)"> jobMapper.listJob(jobName); PageInfo pageInfo </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PageInfo(jobList); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pageInfo; } @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result saveJob(QuartzJob quartz){ </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果是修改 展示旧的 任务</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(quartz.getOldJobGroup() != <span style="color: rgba(0, 0, 255, 1)">null</span> && !""<span style="color: rgba(0, 0, 0, 1)">.equals(quartz.getOldJobGroup())){ JobKey key </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JobKey(quartz.getOldJobName(),quartz.getOldJobGroup()); scheduler.deleteJob(key); } </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">构建job信息</span> Class cls =<span style="color: rgba(0, 0, 0, 1)"> Class.forName(quartz.getJobClassName()) ; cls.newInstance(); JobDetail job </span>=<span style="color: rgba(0, 0, 0, 1)"> JobBuilder.newJob(cls).withIdentity(quartz.getJobName(), quartz.getJobGroup()) .withDescription(quartz.getDescription()).build(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 触发时间点</span> CronScheduleBuilder cronScheduleBuilder =<span style="color: rgba(0, 0, 0, 1)"> CronScheduleBuilder.cronSchedule(quartz.getCronExpression().trim()); Trigger trigger </span>= TriggerBuilder.newTrigger().withIdentity("trigger"+<span style="color: rgba(0, 0, 0, 1)">quartz.getJobName(), quartz.getJobGroup()) .startNow().withSchedule(cronScheduleBuilder).build(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">交由Scheduler安排触发</span>
scheduler.scheduleJob(job, trigger);
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}@Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result triggerJob(String jobName, String jobGroup) { JobKey key </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JobKey(jobName,jobGroup); </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { scheduler.triggerJob(key); } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SchedulerException e) { e.printStackTrace(); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.error(); } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.ok(); } @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result pauseJob(String jobName, String jobGroup) { JobKey key </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JobKey(jobName,jobGroup); </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { scheduler.pauseJob(key); } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SchedulerException e) { e.printStackTrace(); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.error(); } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.ok(); } @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result resumeJob(String jobName, String jobGroup) { JobKey key </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JobKey(jobName,jobGroup); </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { scheduler.resumeJob(key); } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SchedulerException e) { e.printStackTrace(); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.error(); } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.ok(); } @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result removeJob(String jobName, String jobGroup) { </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { TriggerKey triggerKey </span>=<span style="color: rgba(0, 0, 0, 1)"> TriggerKey.triggerKey(jobName, jobGroup); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 停止触发器</span>
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
System.out.println("removeJob:"+JobKey.jobKey(jobName));
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}
}
主要就是以上文件,详情请查看spring-boot-quartz
工程里面数据源用的 druid,springboot 默认也会将该数据源应用到 quartz,如果想给 quartz 单独配置数据源,可配合 @QuartzDataSource 来实现(更多 quarz 数据源问题,请查看spring-boot-2.0.3 之 quartz 集成,数据源问题,源码探究)
最终效果如下
trigger 状态
org.quartz.impl.jdbcjobstore.Constants 中存放了一些列的常量,源代码如下
/* * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */package org.quartz.impl.jdbcjobstore;
/**
-
<p>
-
This interface can be implemented by any <code>{@link
-
org.quartz.impl.jdbcjobstore.DriverDelegate}</code>
-
class that needs to use the constants contained herein.
-
</p>
-
@author <a href="mailto:jeff@binaryfeed.org">Jeffrey Wescott</a>
-
@author James House
*/
public interface Constants {/*
-
- Constants.
-
*/
// Table names
String TABLE_JOB_DETAILS = "JOB_DETAILS";String TABLE_TRIGGERS = "TRIGGERS";
String TABLE_SIMPLE_TRIGGERS = "SIMPLE_TRIGGERS";
String TABLE_CRON_TRIGGERS = "CRON_TRIGGERS";
String TABLE_BLOB_TRIGGERS = "BLOB_TRIGGERS";
String TABLE_FIRED_TRIGGERS = "FIRED_TRIGGERS";
String TABLE_CALENDARS = "CALENDARS";
String TABLE_PAUSED_TRIGGERS = "PAUSED_TRIGGER_GRPS";
String TABLE_LOCKS = "LOCKS";
String TABLE_SCHEDULER_STATE = "SCHEDULER_STATE";
// TABLE_JOB_DETAILS columns names
-
String COL_JOB_NAME </span>= "JOB_NAME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_JOB_GROUP </span>= "JOB_GROUP"<span style="color: rgba(0, 0, 0, 1)">;
String COL_IS_DURABLE </span>= "IS_DURABLE"<span style="color: rgba(0, 0, 0, 1)">;
String COL_IS_VOLATILE </span>= "IS_VOLATILE"<span style="color: rgba(0, 0, 0, 1)">;
String COL_IS_NONCONCURRENT </span>= "IS_NONCONCURRENT"<span style="color: rgba(0, 0, 0, 1)">;
String COL_IS_UPDATE_DATA </span>= "IS_UPDATE_DATA"<span style="color: rgba(0, 0, 0, 1)">;
String COL_REQUESTS_RECOVERY </span>= "REQUESTS_RECOVERY"<span style="color: rgba(0, 0, 0, 1)">;
String COL_JOB_DATAMAP </span>= "JOB_DATA"<span style="color: rgba(0, 0, 0, 1)">;
String COL_JOB_CLASS </span>= "JOB_CLASS_NAME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_DESCRIPTION </span>= "DESCRIPTION"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_TRIGGERS columns names</span>
String COL_TRIGGER_NAME = "TRIGGER_NAME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_TRIGGER_GROUP </span>= "TRIGGER_GROUP"<span style="color: rgba(0, 0, 0, 1)">;
String COL_NEXT_FIRE_TIME </span>= "NEXT_FIRE_TIME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_PREV_FIRE_TIME </span>= "PREV_FIRE_TIME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_TRIGGER_STATE </span>= "TRIGGER_STATE"<span style="color: rgba(0, 0, 0, 1)">;
String COL_TRIGGER_TYPE </span>= "TRIGGER_TYPE"<span style="color: rgba(0, 0, 0, 1)">;
String COL_START_TIME </span>= "START_TIME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_END_TIME </span>= "END_TIME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_PRIORITY </span>= "PRIORITY"<span style="color: rgba(0, 0, 0, 1)">;
String COL_MISFIRE_INSTRUCTION </span>= "MISFIRE_INSTR"<span style="color: rgba(0, 0, 0, 1)">;
String ALIAS_COL_NEXT_FIRE_TIME </span>= "ALIAS_NXT_FR_TM"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_SIMPLE_TRIGGERS columns names</span>
String COL_REPEAT_COUNT = "REPEAT_COUNT"<span style="color: rgba(0, 0, 0, 1)">;
String COL_REPEAT_INTERVAL </span>= "REPEAT_INTERVAL"<span style="color: rgba(0, 0, 0, 1)">;
String COL_TIMES_TRIGGERED </span>= "TIMES_TRIGGERED"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_CRON_TRIGGERS columns names</span>
String COL_CRON_EXPRESSION = "CRON_EXPRESSION"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_BLOB_TRIGGERS columns names</span>
String COL_BLOB = "BLOB_DATA"<span style="color: rgba(0, 0, 0, 1)">;
String COL_TIME_ZONE_ID </span>= "TIME_ZONE_ID"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_FIRED_TRIGGERS columns names</span>
String COL_INSTANCE_NAME = "INSTANCE_NAME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_FIRED_TIME </span>= "FIRED_TIME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_SCHED_TIME </span>= "SCHED_TIME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_ENTRY_ID </span>= "ENTRY_ID"<span style="color: rgba(0, 0, 0, 1)">;
String COL_ENTRY_STATE </span>= "STATE"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_CALENDARS columns names</span>
String COL_CALENDAR_NAME = "CALENDAR_NAME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_CALENDAR </span>= "CALENDAR"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_LOCKS columns names</span>
String COL_LOCK_NAME = "LOCK_NAME"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_LOCKS columns names</span>
String COL_LAST_CHECKIN_TIME = "LAST_CHECKIN_TIME"<span style="color: rgba(0, 0, 0, 1)">;
String COL_CHECKIN_INTERVAL </span>= "CHECKIN_INTERVAL"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> MISC CONSTANTS</span>
String DEFAULT_TABLE_PREFIX = "QRTZ_"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> STATES</span>
String STATE_WAITING = "WAITING"<span style="color: rgba(0, 0, 0, 1)">;
String STATE_ACQUIRED </span>= "ACQUIRED"<span style="color: rgba(0, 0, 0, 1)">;
String STATE_EXECUTING </span>= "EXECUTING"<span style="color: rgba(0, 0, 0, 1)">;
String STATE_COMPLETE </span>= "COMPLETE"<span style="color: rgba(0, 0, 0, 1)">;
String STATE_BLOCKED </span>= "BLOCKED"<span style="color: rgba(0, 0, 0, 1)">;
String STATE_ERROR </span>= "ERROR"<span style="color: rgba(0, 0, 0, 1)">;
String STATE_PAUSED </span>= "PAUSED"<span style="color: rgba(0, 0, 0, 1)">;
String STATE_PAUSED_BLOCKED </span>= "PAUSED_BLOCKED"<span style="color: rgba(0, 0, 0, 1)">;
String STATE_DELETED </span>= "DELETED"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* </span><span style="color: rgba(128, 128, 128, 1)">@deprecated</span><span style="color: rgba(0, 128, 0, 1)"> Whether a trigger has misfired is no longer a state, but
* rather now identified dynamically by whether the trigger's next fire
* time is more than the misfire threshold time in the past.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String STATE_MISFIRED </span>= "MISFIRED"<span style="color: rgba(0, 0, 0, 1)">;
String ALL_GROUPS_PAUSED </span>= "_$_ALL_GROUPS_PAUSED_$_"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TRIGGER TYPES</span>
<span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Simple Trigger type. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String TTYPE_SIMPLE </span>= "SIMPLE"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Cron Trigger type. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String TTYPE_CRON </span>= "CRON"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Calendar Interval Trigger type. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String TTYPE_CAL_INT </span>= "CAL_INT"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Daily Time Interval Trigger type. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String TTYPE_DAILY_TIME_INT </span>= "DAILY_I"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> A general blob Trigger type. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String TTYPE_BLOB </span>= "BLOB"<span style="color: rgba(0, 0, 0, 1)">;
}
// EOF
里面有 quartz 的表名、各个表包含的列名、trigger 状态、trigger 类型等内容
状态包括
WAITING:等待中
ACQUIRED:将触发,此时还未到 trigger 真正的触发时刻
EXECUTING:触发,亦可理解成执行中,trigger 真正的触发时刻
COMPLETE:完成,不再触发
BLOCKED:受阻,不允许并发执行 job 时会出现(@DisallowConcurrentExecution)
ERROR:出错
PAUSED:暂停中
PAUSED_BLOCKED:暂停受阻,不允许并发执行 job 时会出现(@DisallowConcurrentExecution)
DELETED:已删除
MISFIRED:触发失败,已弃用,有另外的替代方式
状态变化流程图如下所示
trigger 的初始状态是 WAITING,处于 WAITING 状态的 trigger 等待被触发。调度线程会不停地扫 triggers 表,根据 NEXT_FIRE_TIME 提前拉取即将触发的 trigger,如果这个 trigger 被该调度线程拉取到,它的状态就会变为 ACQUIRED。因为是提前拉取 trigger,并未到达 trigger 真正的触发时刻,所以调度线程会等到真正触发的时刻,再将 trigger 状态由 ACQUIRED 改为 EXECUTING。如果这个 trigger 不再执行,就将状态改为 COMPLETE, 否则为 WAITING,开始新的周期。如果这个周期中的任何环节抛出异常,trigger 的状态会变成 ERROR。如果手动暂停这个 trigger,状态会变成 PAUSED。
总结
Quartz 作为一个开源的作业调度框架,提供了巨大的灵活性而不牺牲简单性。我们能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库、集群、插件、JavaMail 支持,EJB 作业预构建,支持 cron-like 表达式等等;
springboot 集成 quartz 非常简单,最简单的情况下只需要引入依赖我们就可以享受 quartz 提供的功能,springboot 默认会帮我们配置好 quartz;当然我们也可以自定义配置来实现 quartz 的定制;