Quartz架构介绍
1.关于Quartz
Quartz
是OpenSymphony
开源组织在Job scheduling
领域开源的一个作业调度框架项目,完全由Java
编写,主要是为了实现在Java
- 应用程序中进行作业调度并提供了简单却强大的机制!
Quartz
不仅可以单独使用,还可以与J2EE
与J2SE
应用程序相结合使用!同时,Quartz
允许程序开发人员根据时间的间隔来调度作业!- 与
JDK
中调度器不同的是,Quartz
实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联,一次可以调度几十个、上百个甚至上几万个复杂的程序!
2.Quartz架构
从图中可以看出,Quartz 框架主要包括如下几个部分:
SchedulerFactory
:任务调度工厂,主要负责管理任务调度器Scheduler
:任务调度控制器,主要是负责任务调度Job
:任务接口,即被调度的任务JobDetail
:Job
的任务描述类,job
执行时会依据此对象的信息反射实例化出Job
的具体执行对象Trigger
:任务触发器,主要存放Job
执行的时间策略。例如多久执行一次,什么时候执行,以什么频率执行等等Calendar
:Trigger
扩展对象,可以排除或者包含某个指定的时间点(如排除法定节假日)JobStore
:存储作业和任务调度期间的状态
我们通过一个简单的例子帮助大家理解一下,示例如下
public class QuartzTest implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
public static void main(String[] args) throws SchedulerException {
// 创建一个Scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 启动Scheduler
scheduler.start();
// 新建一个Job, 指定执行类是QuartzTest, 指定一个K/V类型的数据, 指定job的name和group
JobDetail job = JobBuilder.newJob(QuartzTest.class)
.usingJobData("jobData", "test")
.withIdentity("myJob", "myJobGroup")
.build();
// 新建一个Trigger, 表示JobDetail的调度计划, 这里的cron表达式是 每1秒执行一次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "myTriggerGroup")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
// 让scheduler开始调度这个job, 按trigger指定的计划
scheduler.scheduleJob(job, trigger);
}
}
输出效果如下
2020-11-09 21:38:40
2020-11-09 21:38:45
2020-11-09 21:38:50
2020-11-09 21:38:55
2020-11-09 21:39:00
2020-11-09 21:39:05
2020-11-09 21:39:10
... ...
在应用方面使用最多的就是Job
和Trigger
3.源码解读
3.1 Job
Job的源码比较简单,其内部就是一个包含执行方法void execute(JobExecutionContext context)
的接口,开发者只需要实现接口来定义具体任务即可。
public interface Job {
void execute(JobExecutionContext context) throws JobExecutionException;
}
JobExecutionContext
类封装了获取上下文的各种信息,Job
运行时的信息也保存在JobDataMap
示例中。比如我想获取到在上文初始化时使用到的参数,可以通过如下方式获取。
public void execute(JobExecutionContext context) throws JobExecutionException {
//从context中获取instName,groupName以及dataMap
String jobName = context.getJobDetail().getKey().getName();
String groupName = context.getJobDetail().getKey().getGroup();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
//从dataMap中获取myDescription,myValue以及myArray
String value = dataMap.getString("jobData");
System.out.println("jobName:" + jobName + ",groupName:" + groupName + ",jobData:" + value);
}
3.2 Trigger
Trigger
主要用于描述Job
执行的时间触发规则,最常用的有SimpleTrigger
和CronTrigger
两个实现类型.
SimpleTrigger
:主要处理一些简单的调度规则,例如触发一次或者以固定时间间隔周期执行CronTrigger
:调度处理更加灵活,可以通过Cron表达式定义出各种复杂时间规则的调度方案,例如每早晨9:00执行,周一、周三、周五下午5:00执行等
3.2.1 SimpleTrigger
例如使用SimpleTrigger实现没2s执行一次任务,代码如下
public static void main(String[] args) throws SchedulerException {
//1.创建一个Scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//2.启动Scheduler
scheduler.start();
//3.新建一个Job, 指定执行类是QuartzTest, 指定一个K/V类型的数据, 指定job的name和group
JobDetail job = JobBuilder.newJob(QuartzTest.class)
.usingJobData("jobData", "test")
.withIdentity("myJob", "myJobGroup")
.build();
//4.构建一个Trigger,指定Trigger名称和组,规定该Job立即执行,且两秒钟重复执行一次
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.startNow() // 执行的时机,立即执行
.withIdentity("myTrigger", "myTriggerGroup") // 不是必须的
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever())
.build();
//5.让scheduler开始调度这个job, 按trigger指定的计划
scheduler.scheduleJob(job, trigger);
}
其中最关键的就是withSchedule()
方法,通过SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()
来构建一个简单的SimpleTrigger
类型的任务调度规则,从而实现任务调度。
3.2.2 CronTrigger
使用CronTrigger实现每5s执行一次任务,代码如下
public static void main(String[] args) throws SchedulerException {
//1.创建一个Scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//2.启动Scheduler
scheduler.start();
//3.新建一个Job, 指定执行类是QuartzTest, 指定一个K/V类型的数据, 指定job的name和group
JobDetail job = JobBuilder.newJob(QuartzTest.class)
.usingJobData("jobData", "test")
.withIdentity("myJob", "myJobGroup")
.build();
//4.构建一个Trigger,指定Trigger名称和组,规定该Job立即执行,且两秒钟重复执行一次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "myTriggerGroup")
.startNow() // 立即执行
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
//5.让scheduler开始调度这个job, 按trigger指定的计划
scheduler.scheduleJob(job, trigger);
}
相比于SimpleTrigger
,在配置调度规则方面,使用cron
表达式更加灵活。
可以参考:Cron表达式详解
3.3 监听器(选用)
quartz
除了提供能正常调度任务的功能之外,还提供了监听器功能。
所谓监听器,其实你可以把它理解为类似Spring Aop
的功能,可以对全局或者局部实现监听。
监听器应用,在实际项目中并不常用,但是在某些业务场景下,可以发挥一定的作用,例如:你想在任务处理完成之后,去发送邮件或者发短信进行通知,但是你又不想改以前的代码,这个时候就可以在监听器里面完成改项任务。
quartz 监听器主要分三大类:
SchedulerListener
:任务调度监听器TriggerListener
:任务触发监听器JobListener
:任务执行监听器
3.3.1 SchedulerListener
SchedulerListener
监听器,主要对任务调度Scheduler
生命周期中关键节点进行监听,它只能全局进行监听,示例如下
public class SimpleSchedulerListener implements SchedulerListener {
@Override
public void jobScheduled(Trigger trigger) {
System.out.println("任务被部署时被执行");
}
@Override
public void jobUnscheduled(TriggerKey triggerKey) {
System.out.println("任务被卸载时被执行");
}
@Override
public void triggerFinalized(Trigger trigger) {
System.out.println("任务完成了它的使命,光荣退休时被执行");
}
@Override
public void triggerPaused(TriggerKey triggerKey) {
System.out.println(triggerKey + "(一个触发器)被暂停时被执行");
}
@Override
public void triggersPaused(String triggerGroup) {
System.out.println(triggerGroup + "所在组的全部触发器被停止时被执行");
}
@Override
public void triggerResumed(TriggerKey triggerKey) {
System.out.println(triggerKey + "(一个触发器)被恢复时被执行");
}
@Override
public void triggersResumed(String triggerGroup) {
System.out.println(triggerGroup + "所在组的全部触发器被回复时被执行");
}
@Override
public void jobAdded(JobDetail jobDetail) {
System.out.println("一个JobDetail被动态添加进来");
}
@Override
public void jobDeleted(JobKey jobKey) {
System.out.println(jobKey + "被删除时被执行");
}
@Override
public void jobPaused(JobKey jobKey) {
System.out.println(jobKey + "被暂停时被执行");
}
@Override
public void jobsPaused(String jobGroup) {
System.out.println(jobGroup + "(一组任务)被暂停时被执行");
}
@Override
public void jobResumed(JobKey jobKey) {
System.out.println(jobKey + "被恢复时被执行");
}
@Override
public void jobsResumed(String jobGroup) {
System.out.println(jobGroup + "(一组任务)被恢复时被执行");
}
@Override
public void schedulerError(String msg, SchedulerException cause) {
System.out.println("出现异常" + msg + "时被执行");
cause.printStackTrace();
}
@Override
public void schedulerInStandbyMode() {
System.out.println("scheduler被设为standBy等候模式时被执行");
}
@Override
public void schedulerStarted() {
System.out.println("scheduler启动时被执行");
}
@Override
public void schedulerStarting() {
System.out.println("scheduler正在启动时被执行");
}
@Override
public void schedulerShutdown() {
System.out.println("scheduler关闭时被执行");
}
@Override
public void schedulerShuttingdown() {
System.out.println("scheduler正在关闭时被执行");
}
@Override
public void schedulingDataCleared() {
System.out.println("scheduler中所有数据包括jobs, triggers和calendars都被清空时被执行");
}
}
需要在任务调度器启动前,将SimpleSchedulerListener
注册到Scheduler
容器中
...
//1.创建一个Scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//2.添加SchedulerListener监听器
scheduler.getListenerManager().addSchedulerListener(new SimpleSchedulerListener());
//3.启动Scheduler
scheduler.start();
...
3.3.2 TriggerListener
TriggerListener
与触发器Trigger
相关的事件都会被监听,它既可以全局监听,也可以实现局部监听,所谓局部监听,就是对某个Trigger
的名称或者组进行监听,简单示例如下:
public class SimpleTriggerListener implements TriggerListener {
//Trigger监听器的名称
@Override
public String getName() {
return "mySimpleTriggerListener";
}
//Trigger被激发 它关联的job即将被运行
@Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
System.out.println("myTriggerListener.triggerFired()");
}
//Trigger被激发 它关联的job即将被运行, TriggerListener 给了一个选择去否决 Job 的执行,如果返回TRUE 那么任务job会被终止
@Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
System.out.println("myTriggerListener.vetoJobExecution()");
return false;
}
//当Trigger错过被激发时执行,比如当前时间有很多触发器都需要执行,但是线程池中的有效线程都在工作,
//那么有的触发器就有可能超时,错过这一轮的触发。
@Override
public void triggerMisfired(Trigger trigger) {
System.out.println("myTriggerListener.triggerMisfired()");
}
//任务完成时触发
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
System.out.println("myTriggerListener.triggerComplete()");
}
}
同样的也需要注册到Scheduler
容器中。
...
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 创建并注册一个全局的Trigger Listener
scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener(), EverythingMatcher.allTriggers());
// 创建并注册一个局部的Trigger Listener
//scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("myTrigger", "myJobTrigger")));
// 创建并注册一个特定组的Trigger Listener
//scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener(), GroupMatcher.groupEquals("myTrigger"));
// 启动Scheduler
scheduler.start();
...
3.3.3 JobListener
JobListener
与任务执行Job
相关的事件都会被监听,和Trigger
一样,既可以全局监听,也可以实现局部监听。示例如下:
public class SimpleJobListener implements JobListener {
//job监听器名称
@Override
public String getName() {
return "mySimpleJobListener";
}
//任务被调度前
@Override
public void jobToBeExecuted(JobExecutionContext context) {
System.out.println("simpleJobListener监听器,准备执行:"+context.getJobDetail().getKey());
}
//任务调度被拒了
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
System.out.println("simpleJobListener监听器,取消执行:"+context.getJobDetail().getKey());
}
//任务被调度后
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
System.out.println("simpleJobListener监听器,执行结束:"+context.getJobDetail().getKey());
}
}
同样注册到Scheduler容器中,代码如下
...
// 创建一个Scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 创建并注册一个全局的Job Listener
scheduler.getListenerManager().addJobListener(new SimpleJobListener(), EverythingMatcher.allJobs());
// 创建并注册一个指定任务的Job Listener
//scheduler.getListenerManager().addJobListener(new SimpleJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("myJob", "myJobGroup")));
// 将同一任务组的任务注册到监听器中
//scheduler.getListenerManager().addJobListener(new SimpleJobListener(), GroupMatcher.jobGroupEquals("myJobGroup"));
// 启动Scheduler
scheduler.start();
...
3.3.4 多个同时监听
如果想要注册多个监听,将其依次加入皆苦,示例如下:
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//添加SchedulerListener监听器
scheduler.getListenerManager().addSchedulerListener(new SimpleSchedulerListener());
// 创建并注册一个全局的Trigger Listener
scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener(), EverythingMatcher.allTriggers());
// 创建并注册一个全局的Job Listener
scheduler.getListenerManager().addJobListener(new SimpleJobListener(), EverythingMatcher.allJobs());
// 启动Scheduler
scheduler.start();
单体应用(SpringBoot + Quartz)
1 引入Quartz包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2 实现的具体业务
public class TestTask implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("testTask:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
3 编写任务调度配置服务
@Configuration
public class TestTaskConfig {
@Bean
public JobDetail testQuartz() {
return JobBuilder.newJob(TestTask.class)
.usingJobData("jobData", "test") //数据
.withIdentity("myJob", "myJobGroup") //任务名称及分组
.storeDurably() //信息持久化,默认为false
.build();
}
@Bean
public Trigger testQuartzTrigger() {
//5秒执行一次
return TriggerBuilder.newTrigger()
.forJob(testQuartz())
.withIdentity("myTrigger", "myTriggerGroup") //触发器名称及分组
.startNow() //立即执行
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
}
}
注意:存在的问题,如果需要创建多个定时任务,我们就需要按照此流程进行多次配置,这种静态配置多了,就会给后维护带来不便,比如,我任务的执行周期发生变动,这个时候就需要修改代码,但是我们又不想修改代码,那怎么办呢?
4 动态配置定时任务
上边我们说到,静态配置一旦发生需求变动,我们就必须修改代码,那么为了更好的维护代码,减轻我们维护人员的工作压力。我们就需要 利用数据库来实现动态的定时任务。
从以上的代码阅读中不难看出,定时任务存在三个核心变量,其他的方法都是可以封装的。
- 任务名称:例如
myJob
- 任务执行类:例如
TestTask.class
- 任务调度时间:例如
0/5 * * * * ?
基于此,我们可以创建一个定时任务的实体类,用于保存定时任务相关的信息到数据库中,然后编写一个定时任务工具库,用于创建、更新、删除、暂停任务操作。通过restful接口操作将任务存入数据库并管理定时任务。
4.1 引入包
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
4.2 添加数据库配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/netty?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
4.3 创建任务配置表
CREATE TABLE `tb_job_task` (
`id` varchar(50) NOT NULL COMMENT '任务ID',
`job_name` varchar(100) DEFAULT NULL COMMENT '任务名称',
`job_class` varchar(200) DEFAULT NULL COMMENT '任务执行类',
`cron_expression` varchar(50) DEFAULT NULL COMMENT '任务调度时间表达式',
`status` int(4) DEFAULT NULL COMMENT '任务状态,0:启动,1:暂停,2:停用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
4.4 编写任务实体类
public class QuartzBean {
private String id;
private String jobName;
private String jobClass;
private String cronExpression;
private Integer status;
//get、set...
}
4.5 编写任务操作工具类
public class QuartzUtils {
private static final Logger log = LoggerFactory.getLogger(QuartzUtils.class);
/**
* 创建定时任务 定时任务创建之后默认启动状态
* @param scheduler 调度器
* @param quartzBean 定时任务信息类
*/
public static void createScheduleJob(Scheduler scheduler, QuartzBean quartzBean){
try {
//获取到定时任务的执行类 必须是类的绝对路径名称
//定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。
Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzBean.getJobClass());
// 构建定时任务信息
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartzBean.getJobName()).build();
// 设置定时任务执行方式
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
// 构建触发器trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzBean.getJobName()).withSchedule(scheduleBuilder).build();
scheduler.scheduleJob(jobDetail, trigger);
} catch (ClassNotFoundException e) {
log.error("定时任务类路径出错:请输入类的绝对路径", e);
} catch (SchedulerException e) {
log.error("创建定时任务出错", e);
}
}
/**
* 根据任务名称暂停定时任务
* @param scheduler 调度器
* @param jobName 定时任务名称
*/
public static void pauseScheduleJob(Scheduler scheduler, String jobName){
JobKey jobKey = JobKey.jobKey(jobName);
try {
scheduler.pauseJob(jobKey);
} catch (SchedulerException e) {
log.error("暂停定时任务出错", e);
}
}
/**
* 根据任务名称恢复定时任务
* @param scheduler 调度器
* @param jobName 定时任务名称
*/
public static void resumeScheduleJob(Scheduler scheduler, String jobName) {
JobKey jobKey = JobKey.jobKey(jobName);
try {
scheduler.resumeJob(jobKey);
} catch (SchedulerException e) {
log.error("暂停定时任务出错", e);
}
}
/**
* 根据任务名称立即运行一次定时任务
* @param scheduler 调度器
* @param jobName 定时任务名称
*/
public static void runOnce(Scheduler scheduler, String jobName){
JobKey jobKey = JobKey.jobKey(jobName);
try {
scheduler.triggerJob(jobKey);
} catch (SchedulerException e) {
log.error("运行定时任务出错", e);
}
}
/**
* 更新定时任务
* @param scheduler 调度器
* @param quartzBean 定时任务信息类
*/
public static void updateScheduleJob(Scheduler scheduler, QuartzBean quartzBean) {
try {
//获取到对应任务的触发器
TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName());
//设置定时任务执行方式
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
//重新构建任务的触发器trigger
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//重置对应的job
scheduler.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
log.error("更新定时任务出错", e);
}
}
/**
* 根据定时任务名称从调度器当中删除定时任务
* @param scheduler 调度器
* @param jobName 定时任务名称
*/
public static void deleteScheduleJob(Scheduler scheduler, String jobName) {
JobKey jobKey = JobKey.jobKey(jobName);
try {
scheduler.deleteJob(jobKey);
} catch (SchedulerException e) {
log.error("删除定时任务出错", e);
}
}
}
4.6 编写controller服务
@RestController
@RequestMapping("/quartz")
public class QuartzController {
private static final Logger log = LoggerFactory.getLogger(QuartzController.class);
@Autowired
private Scheduler scheduler;
@Autowired
private QuartzBeanRepository repository;
/**
* 创建任务
*/
@RequestMapping("/createJob")
public HttpStatus createJob(@RequestBody QuartzBean quartzBean) {
log.info("=========开始创建任务=========");
QuartzUtils.createScheduleJob(scheduler,quartzBean);
repository.save(quartzBean.setId(UUID.randomUUID().toString()).setStatus(0));
log.info("=========创建任务成功,信息:{}", JSON.toJSONString(quartzBean));
return HttpStatus.OK;
}
/**
* 暂停任务
*/
@RequestMapping("/pauseJob")
public HttpStatus pauseJob(@RequestBody QuartzBean quartzBean) {
log.info("=========开始暂停任务,请求参数:{}=========", JSON.toJSONString(quartzBean));
QuartzUtils.pauseScheduleJob(scheduler, quartzBean.getJobName());
repository.updateState(quartzBean.getId(), 1);
log.info("=========暂停任务成功=========");
return HttpStatus.OK;
}
/**
* 立即运行一次定时任务
*/
@RequestMapping("/runOnce")
public HttpStatus runOnce(@RequestBody QuartzBean quartzBean) {
log.info("=========立即运行一次定时任务,请求参数:{}", JSON.toJSONString(quartzBean));
QuartzUtils.runOnce(scheduler,quartzBean.getJobName());
log.info("=========立即运行一次定时任务成功=========");
return HttpStatus.OK;
}
/**
* 恢复定时任务
*/
@RequestMapping("/resume")
public HttpStatus resume(@RequestBody QuartzBean quartzBean) {
log.info("=========恢复定时任务,请求参数:{}", JSON.toJSONString(quartzBean));
QuartzUtils.resumeScheduleJob(scheduler,quartzBean.getJobName());
repository.updateState(quartzBean.getId(), 0);
log.info("=========恢复定时任务成功=========");
return HttpStatus.OK;
}
/**
* 更新定时任务
*/
@RequestMapping("/update")
public HttpStatus update(@RequestBody QuartzBean quartzBean) {
log.info("=========更新定时任务,请求参数:{}", JSON.toJSONString(quartzBean));
QuartzUtils.updateScheduleJob(scheduler,quartzBean);
repository.updateCron(quartzBean.getId(), quartzBean.getCronExpression());
log.info("=========更新定时任务成功=========");
return HttpStatus.OK;
}
/**
* 删除定时任务
*/
@RequestMapping("/delete")
public HttpStatus delete(@RequestBody QuartzBean quartzBean) {
log.info("=========删除定时任务,请求参数:{}", JSON.toJSONString(quartzBean));
QuartzUtils.deleteScheduleJob(scheduler,quartzBean.getJobName());
repository.updateState(quartzBean.getId(), 2);
log.info("=========删除定时任务成功=========");
return HttpStatus.OK;
}
}
5 服务重启补偿
在应用程序正常运行的时候,虽然没问题,但是当我们重启服务的时候,这个时候内存的里面的定时任务其实全部都被销毁,因此在应用程序启动的时候,还需要将正常的任务重新加入到服务中。
@Component
public class TaskConfigApplicationRunner implements ApplicationRunner {
@Autowired
private QuartzBeanRepository repository;
@Autowired
private Scheduler scheduler;
@Override
public void run(ApplicationArguments args) throws Exception {
List<QuartzBean> list = repository.findAll();
if(!CollectionUtils.isEmpty(list)){
for (QuartzBean quartzBean : list) {
//加载启动类型的定时任务
if(quartzBean.getStatus().intValue() == 0){
QuartzUtils.createScheduleJob(scheduler,quartzBean);
}
//加载暂停类型的定时任务
if(quartzBean.getStatus().intValue() == 1){
QuartzUtils.createScheduleJob(scheduler,quartzBean);
QuartzUtils.pauseScheduleJob(scheduler, quartzBean.getJobName());
}
}
}
}
}
6 添加监听器(选用)
@Configuration
public class TestTaskConfig {
@Primary
@Bean
public Scheduler initScheduler() throws SchedulerException {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//添加SchedulerListener监听器
scheduler.getListenerManager().addSchedulerListener(new SimpleSchedulerListener());
// 创建并注册一个全局的Trigger Listener
scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener(), EverythingMatcher.allTriggers());
// 创建并注册一个全局的Job Listener
scheduler.getListenerManager().addJobListener(new SimpleJobListener(), EverythingMatcher.allJobs());
scheduler.start();
return scheduler;
}
}
注意:quartz
任务暂停之后再次启动时,会立即执行一次,在更新之后也会立即执行一次任务调度!
总结
本文主要围绕 quartz
的架构以及Springboot + quartz
项目整合和应用做了初步的介绍,quartz
已经有对应的任务表,不需要手动创建,只需要配置quartz.properties
文件即可!quartz
官方提供了对应任务表,主要用于分布式架构下的任务处理,如果只是单体应用,可以参考本文中创建一张单表来存储任务,实现简单。