一、基本概念
Quartz核心的概念:scheduler任务调度、Job任务、Trigger触发器、JobDetail任务细节。
scheduler任务调度:
是最核心的概念,需要把JobDetail和Trigger注册到scheduler中,才可以执行。
Job任务:
其实Job是接口,其中只有一个execute方法:
Trigger触发器
a)作用:它是来执行工作任务,在什么条件下触发,什么时间执行,多久执行一次。
b)四大类型:SimpleTrigger,CronTirgger,DateIntervalTrigger, 和 NthIncludedDayTrigger。
SimpleTrigger 一般用于实现每隔一定时间执行任务,以及重复多少次,如每 2 小时执行一次,重复执行 5 次。SimpleTrigger 内部实现机制是通过计算间隔时间来计算下次的执行时间,这就导致其不适合调度定时的任务。例如我们想每天的 1:00AM 执行任务,如果使用 SimpleTrigger 的话间隔时间就是一天。注意这里就会有一个问题,即当有 misfired 的任务并且恢复执行时,该执行时间是随机的(取决于何时执行 misfired 的任务,例如某天的 3:00PM)。这会导致之后每天的执行时间都会变成 3:00PM,而不是我们原来期望的 1:00AM。
CronTirgger 类似于 LINUX 上的任务调度命令 crontab,即利用一个包含 7 个字段的表达式来表示时间调度方式。例如,"0 15 10 * * ? *" 表示每天的 10:15AM 执行任务。对于涉及到星期和月份的调度,CronTirgger 是最适合的,甚至某些情况下是唯一选择。例如,"0 10 14 ? 3 WED" 表示三月份的每个星期三的下午 14:10PM 执行任务。读者可以在具体用到该 trigger 时再详细了解每个字段的含义
整合案例
项目结构
listener的作用
每次项目重启后,让原来处于运行中的任务,继续运行
QuartzManager的作用
对定时任务执行具体操作的工具类
依赖
org.quartz-scheduler
quartz
2.3.1
org.quartz-scheduler
quartz-jobs
2.3.1
application.yml配置
#quartz
spring:
quartz:
scheduler-name: my-project-scheduler
job-store-type: jdbc
auto-startup: false
wait-for-jobs-to-complete-on-shutdown: false
overwrite-existing-jobs: false
jdbc:
#druid的wall filter会影响这里的自动建表
initialize-schema: always
properties:
org:
quartz:
scheduler:
instanceId: AUTO
wrapJobExecutionInUserTransaction: false
rmi:
export: false
proxy: false
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
useProperties: true
tablePrefix: QRTZ_
misfireThreshold: 60000
quartzManager工具类
package com.nanc.modules.schedule.util;
import com.nanc.modules.schedule.entity.ScheduleJobInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class QuartzManager {
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
public static final String JOB_GROUP_NAME = "EXTJWEB_JOBGROUP_NAME";
public static final String TRIGGER_GROUP_NAME = "EXTJWEB_TRIGGERGROUP_NAME";
/**
* 添加任务,使用任务组名(不存在就用默认的),触发器名,触发器组名
* 并启动
* @param info
*/
public void addJob(ScheduleJobInfo info) {
try {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(info.getJobName(), info.getGroupName());
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (null != jobDetail) {
log.info("{}, {} 定时任务已经存在", info.getJobName(), info.getGroupName());
return;
}
JobDataMap map = new JobDataMap();
map.put("taskData", "测试的存放的数据");
// JobDetail 是具体Job实例
jobDetail = JobBuilder.newJob((Class extends Job>) Class.forName(info.getClassName()))
.withIdentity(info.getJobName(),info.getGroupName())
.usingJobData(map)
.build();
// 基于表达式构建触发器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(info.getCron());
// CronTrigger表达式触发器 继承于Trigger
// TriggerBuilder 用于构建触发器实例
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(info.getJobName()+"_trigger", TRIGGER_GROUP_NAME)
.withSchedule(cronScheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
//启动
if (!scheduler.isShutdown()) {
scheduler.start();
}
} catch (SchedulerException | ClassNotFoundException e) {
log.error("添加任务失败",e);
}
}
/**
* 暂停任务
* @param info
*/
public void pauseJob(ScheduleJobInfo info) {
try{
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(info.getJobName(), info.getGroupName());
scheduler.pauseJob(jobKey);
log.info("=========================pause job: {} success========================", info.getJobName());
}catch (Exception e){
log.error("",e);
}
}
/**
* 恢复任务
* @param info
*/
public void resumeJob(ScheduleJobInfo info) {
try {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(info.getJobName(), info.getGroupName());
scheduler.resumeJob(jobKey);
log.info("=========================resume job: {} success========================", info.getJobName());
}catch (Exception e){
log.error("",e);
}
}
/**
* 删除任务,在业务逻辑中需要更新库表的信息
* @param info
* @return
*/
public boolean removeJob(ScheduleJobInfo info) {
boolean result = true;
try {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(info.getJobName(), info.getGroupName());
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (null != jobDetail) {
result = scheduler.deleteJob(jobKey);
}
log.info("=========================remove job: {} {}========================", info.getJobName(), result);
}catch (Exception e){
log.error("",e);
result=false;
}
return result;
}
/**
* 修改定时任务的时间
* @param info
* @return
*/
public boolean modifyJobTime(ScheduleJobInfo info){
boolean result = true;
try{
Scheduler scheduler = schedulerFactoryBean.getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(info.getJobName() + "_trigger", TRIGGER_GROUP_NAME);
CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);
String oldTime = trigger.getCronExpression();
if (!StringUtils.equalsIgnoreCase(oldTime, info.getCron())) {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(info.getCron());
CronTrigger ct = TriggerBuilder.newTrigger().withIdentity(info.getJobName()+ RandomStringUtils.randomAlphabetic(6) + "_trigger", TRIGGER_GROUP_NAME)
.withSchedule(cronScheduleBuilder)
.build();
scheduler.rescheduleJob(triggerKey, ct);
scheduler.resumeTrigger(triggerKey);
}
}catch (Exception e){
log.error("", e);
result=false;
}
return result;
}
/**
* 启动所有定时任务
*/
public void startJobs() {
try {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
scheduler.start();
} catch (SchedulerException e) {
log.error("",e);
}
}
}
定义的实体类
package com.nanc.modules.schedule.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import java.io.Serializable;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@TableName("schedule_job_info")
@ApiModel(description = "定时任务实体类")
public class ScheduleJobInfo implements Serializable {
private static final long serialVersionUID = -1465015133146616824L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 任务group的名称
*/
@NotEmpty(message = "任务group的名称不能为空")
@ApiModelProperty(value = "任务group的名称", example = "test-group")
private String groupName;
/**
* 任务job的名称
*/
@NotEmpty(message = "任务job的名称不能为空")
@ApiModelProperty(value = "任务job的名称", required = true, example = "test-job")
private String jobName;
/**
* 定时任务标识
*/
@NotEmpty(message = "定时任务标识不能为空")
@ApiModelProperty(value = "任务的标识", required = true, example = "schedule-job-code")
private String code;
/**
* cron 表达式
*/
@NotEmpty(message = "定时任务标识不能为空")
@ApiModelProperty(value = "cron表达式", required = true, example = "*/5 * * * * ?")
private String cron;
/**
* 定时任务执行类
*/
@NotEmpty(message = "定时任务标识不能为空")
@ApiModelProperty(value = "定时任务执行类", required = true, example = "com.nanc.modules.schedule.job.CronTestJob")
private String className;
/**
* 成功执行次数
*/
@ApiModelProperty(hidden = true)
private Integer succeed;
/**
* 失败执行次数
*/
@ApiModelProperty(hidden = true)
private Integer fail;
/**
* 任务的状态
* 0 - 代表正在执行
* 1 - 已删除
* 2 - 暂停
*/
@ApiModelProperty(hidden = true)
private Integer status;
/**
* 任务创建的时间
*/
@ApiModelProperty(hidden = true)
private Date createTime;
/**
* 任务修改的时间
*/
@ApiModelProperty(hidden = true)
private Date updateTime;
}
控制层代码
package com.nanc.modules.schedule.controller;
import com.nanc.common.utils.R;
import com.nanc.modules.schedule.entity.ScheduleJobInfo;
import com.nanc.modules.schedule.service.ScheduleJobService;
import com.nanc.modules.schedule.util.QuartzManager;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Slf4j
@RestController
@RequestMapping("/quartz")
@Api(value = "定时任务", tags = "定时任务", position = 5)
public class ScheduleJobController {
@Autowired
private QuartzManager quartzManager;
@Autowired
private ScheduleJobService scheduleJobService;
@ApiOperation(value = "添加定时任务并启动", notes = "添加定时任务并启动")
@PostMapping("/add")
public R addAndStartJob(@Valid @RequestBody ScheduleJobInfo info){
boolean b = scheduleJobService.addAndStartJob(info);
return R.ok(b);
}
/**
* 暂停任务的运行
* @param id
* @return
*/
@ApiOperation(value = "暂停任务", notes = "暂停任务")
@PostMapping("/pause/{id}")
public R pauseJob(@PathVariable(value = "id") Integer id) {
boolean b = scheduleJobService.pauseJob(id);
return R.ok(b);
}
/**
* 暂停任务的运行
* @param id
* @return
*/
@ApiOperation(value = "恢复任务", notes = "恢复任务")
@PostMapping("/resume/{id}")
public R resumeJob(@PathVariable(value = "id") Integer id) {
boolean b = scheduleJobService.resumeJob(id);
return R.ok(b);
}
/**
* 删除任务
* @param id
* @return
*/
@ApiOperation(value = "删除任务", notes = "删除任务")
@PostMapping("/remove/{id}")
public R removeJob(@PathVariable(value = "id") Integer id) {
boolean b = scheduleJobService.removeJob(id);
return R.ok(b);
}
/**
* 修改定时任务的时间
* @param id
* @param cron
* @return
*/
@ApiOperation(value = "修改任务时间", notes = "修改任务时间")
@PostMapping("/modify")
public R modifyJobTime(@RequestParam(value = "id")Integer id,
@RequestParam(value = "cron")String cron){
boolean b = scheduleJobService.modifyJobTime(id, cron);
return R.ok(b);
}
}
关于swagger的整合,可以看另一篇文章
目前只是简单的实现了部分常用的方法
需要源代码的私信,我会从项目中把这模块提取出来,单独打包发送