Quartz
- 1.核心概念
- 1.1 核心概念图
- 1.2 demo
- 2.Job
- 2.1为什么设计成JobDetail+Job, 而不直接使用Job
- 2.2 间隔执行时, 每次都会创建新的Job实例
- 2.3 定时任务默认都是并发执行的,不会等待上一次任务执行完毕
- 2.3.1 不允许并发执行
- 2.4 在运行时, 通过JobDataMap向Job传递数据
- 2.4.1 持久化JobDetail中的JobDataMap
- 3.触发器
- 3.1 优先级
- 3.2 misfire(毕竟难得点, 一般用默认, 如果有特殊需求, 需要自己去研究)
- 3.2.1 哪些情况会导致misfire
- 3.2.2 有哪些策略
- 4.SpringBoot整合Quartz
- 4.1 数据库表准备
- 4.2 Maven主要依赖
- 4.3 quartz.properties
- 4.4 初始化Scheduler, 并交给IOC容器管理
- 4.5 job任务类
- 4.6 实体类
- 4.7 sevice
- 4.8 controller
1.核心概念
- Scheduler(任务调度器)
- Trigger(触发器)
- JobDetail(任务详情)
- Job(具体任务)
1.Scheduler
任务调度器, 通过触发器(Trigger)
和任务详情(JobDetail)
来调度、暂停、删除任务, 调度器可以看作一个独立运行的容器, 通过将Trigger
和JobDetail
注册到Scheduler
中, 两者在Scheduler中拥有各自的组和名称
组和名称是Scheduler
查询某一对象的依据, Trigger
的组和名称必须唯一, JobDetail
的组和名称也必须唯一(但可以跟Trigger相同, 因为两者是不同类型)
Scheduler是一个接口
2.Trigger
触发器, 描述Job执行的时间触发规则, 有两个子类: SimpleTrigger
和CronTrigger
,
SimpleTrigger适合仅调度一次、以固定时间间隔周期执行的调度
CronTrigger可以通过Cron表达式定义出各种复杂时间规则的调度方案, 适合更灵活、更复杂的任务调度需求
3.JobDetail
任务详情, 包括了任务的唯一标识以及具体要执行的任务, 可以通过JobDataMap往任务中传递数据
4.Job
具体任务, 是一个接口, 只定义一个方法execute()方法包含了执行任务的具体方法
1.1 核心概念图
- Job: 要做什么事情
- JobDetail: 封装这个任务, 设置组和名称
- Trigger: 什么时候去做
- Scheduler:
什么时候
去做什么事情
一个Scheduler可以注册多个JobDetail和Trigger
1.2 demo
- 任务
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("执行任务");
}
}
- 初始化任务详情和触发器, 并装载到调度器中, 让调度器去执行
public static void main(String[] args) {
// 任务详情
JobDetail JobDetail = JobBuilder.newJob(MyJob.class)
.withIdentity("job1", "group1")
.build();
// 触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
// 执行计划, 以1秒的间隔执行, 周期性执行
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
.startNow()
.build();
try {
// 调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(JobDetail, trigger); // 装载任务和触发器
scheduler.start(); // 开启定时任务
} catch (SchedulerException e) {
e.printStackTrace();
}
}
注意点: Scheduler每次执行, 都会根据jobDetail创建一个新的job对象, 这样可以规避并发访问的问题(job是多例的, jobDetail也是多例的)
Quartz定时任务默认都是并发执行的, 不会等待上一个任务执行完毕, 只要时间间隔到了, 就会执行. 如果定时任务执行时间太久, 会长时间占用资源, 导致其他任务阻塞
2.Job
2.1为什么设计成JobDetail+Job, 而不直接使用Job
- 分离任务定义和任务执行逻辑
- JobDetail负责定义任务的元数据,比如任务的名称、分组、描述等信息
- Job则负责实际执行任务的逻辑。
- 这样的分离使得任务的定义和执行可以独立地管理和修改,增加了系统的灵活性和可维护性。
- 规避并发访问的问题, 如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题, 通过使用JobDetail + Job 方式, 每次访问, 都会根据JobDetail创建一个新的Job实例
2.2 间隔执行时, 每次都会创建新的Job实例
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("[job]=" + this.hashCode());
System.out.println("[jobDetail]=" + context.getJobDetail().hashCode());
System.out.println("[trigger]=" + context.getTrigger().hashCode());
}
}
每次的job实例都不是同一个
为什么这么设计?
- 状态隔离
即使上一个任务执行中可能存在的状态残留, 也不会影响到下一次任务执行, 这样可以确保任务执行的可靠性和一致性 - 线程安全
通过每次创建新的job实例, 避免在多线程环境下出现资源共享问题. - 避免资源泄漏
通过每次创建新的Job实例,可以确保任务执行完成后,相关的资源能够被正确释放,避免了潜在的资源泄漏问题。
虽然每次创建新的Job实例会增加一定的资源消耗,但这种设计能够保证任务执行的可靠性和一致性,是一种合理的权衡。同时,Quartz也提供了相关的扩展机制,允许开发人员自定义Job实例的创建和生命周期管理策略,以满足特定的需求。
2.3 定时任务默认都是并发执行的,不会等待上一次任务执行完毕
Quartz的调度器(Scheduler)是多线程的, 并且可以同时执行多个作业. 这种并发执行的特性使得Quartz能够高效地处理大量的定时任务
小实验: 在trigger中设置间隔时间为1秒, 同时设置Job的执行时间为3秒
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("execute:" + new Date());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
每次任务执行, 并没有等上一次执行结束才开始, 这说明了每次执行定时任务都是并发的, 定时任务之间相互隔离
2.3.1 不允许并发执行
@DisallowConcurrentExecution注解
的作用是确保同一个作业(job)类的多个实例不会同时执行.
当一个作业实例正在执行时, 下一个调度的任务将会等待前一个任务执行完成后再执行
- 防止并发执行: 标记了
@DisallowConcurrentExecution
的作业类不会被并发执行, 如果一个作业实例正在执行, 另一个 - 避免资源竞争: 适用于那些需要独占资源或者对共享资源有依赖的作业. 通过防止同一作业类的多个实例同时运行, 可以避免资源竞争和数据不一致的问题
- 确保任务的顺序执行: 对于一些需要按顺序执行的任务,通过使用
@DisallowConcurrentExecution注解
可以确保它们按照预期的顺序执行,而不会出现并发执行导致的问题
@DisallowConcurrentExecution
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("execute:" + new Date());
System.out.println("job=" + this.hashCode());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
2.4 在运行时, 通过JobDataMap向Job传递数据
JobDataMap可以帮助我们在作业(job)运行时传递数据. 它允许你在作业执行过程中向Job实例传递任意参数类型的数据
JobDataMap本质是一个Map. 我们将需要传递的数据包装成键值对, put到JobDataMap中即可
- 步骤一: 创建JobDetail, put键值对, 然后在初始化JobDetail或者Trigger时作为参数传入
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("name", "zhangsan");
jobDataMap.put("age", 15);
jobDataMap.put("money", 128.2);
// 任务详情
JobDetail JobDetail = JobBuilder.newJob(MyJob.class)
.withIdentity("job1", "group1")
.usingJobData(jobDataMap)
.build();
- 步骤二: 在job中获取JobDataMap并访问数据
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDataMap jobData = jobExecutionContext.getJobDetail().getJobDataMap();
String name = jobData.getString("name");
int age = jobData.getInt("age");
double money = jobData.getDouble("money");
System.out.println(name + age + money);
// 从trigger中获取传递的参数
// JobDataMap triggerData = jobExecutionContext.getTrigger().getJobDataMap();
// 将JobDetail和Trigger
// JobDataMap mergedJobDataMap = jobExecutionContext.getMergedJobDataMap();
}
PS: 可以使用getMergedJobDataMap
将jobData和triggerData合并在一起, 但会出现相同key被覆盖的情况
2.4.1 持久化JobDetail中的JobDataMap
@PersistJobDataAfterExecution
是Quartz提供的一个注解,用于标记作业类(Job class), 当一个作业类被标记为@PersistJobDataAfterExecution
时, Quartz将在每次作业执行完成后,将作业实例的JobDataMap中的数据持久化到调度存储中. 这意味着作业实例的状态数据将会在执行完成后被保存下来,并在下一次执行时恢复。
这个注解对于需要跟踪作业状态
或者持久化作业的状态数据
非常有用。例如,如果一个作业在执行过程中累加了一个计数器的值,通过使用@PersistJobDataAfterExecution
注解,你可以确保这个计数器的值在每次作业执行完成后都会被保存下来,而不会丢失。
案例: 在job中累加一个计数器的值
因为Job、JobDetail都是每次执行定时任务时新建的, 所以JobDataMap也不是同一个对象. 所以我们如果想让JobDataMap在一个定时任务中被共享(job、jobDetail), 需要添加@PersistJobDataAfterExecution注解
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 获取jobDataMap
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
// 从中获取参数
int count = jobDataMap.getInt("count");
System.out.println("count:" + count);
// 执行作业逻辑, 累加计数器
count++;
// 将更新后的计数器值放回JobDataMap中
jobDataMap.put("count", count);
}
}
注意: 持久化只针对于JobDetail中的JobDataMap(对于Tigger中的JobDataMap无效)
3.触发器
3.1 优先级
Trigger的优先级主要用于决定当多个Trigger同时触发时调度器的执行顺序
当调度器中有多个Trigger准备就绪,可以被调度执行时,调度器会先选择具有最高优先级的Trigger来执行。如果有多个Trigger具有相同的最高优先级,则调度器会根据调度器的调度策略来确定执行顺序。
需要注意的是,Quartz中Trigger的优先级仅影响多个Trigger同时准备就绪时的执行顺序,并不会影响单个Trigger的执行。优先级较高的Trigger并不会强制中断正在执行的Trigger,而只是影响下一个被选中执行的Trigger。
因此,在Quartz中,通过设置Trigger的优先级,可以在一定程度上控制Trigger的执行顺序,使得具有更高优先级的Trigger更有可能首先被执行。
简单来说: 优先级影响多个同时触发的Trigger的执行顺序, 而不会影响它们的执行时间
Trigger trigger1 = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withPriority(5)
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever())
.build();
// 定义Trigger2,优先级为10
Trigger trigger2 = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.withPriority(10)
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever())
.build();
3.2 misfire(毕竟难得点, 一般用默认, 如果有特殊需求, 需要自己去研究)
misfire指触发器错过触发时间后, Quartz如何处理错过的触发事件
3.2.1 哪些情况会导致misfire
- 当job达到触发时间时, 所有线程都在作业, 没有可用线程
- 在job需要触发的时间点, scheduler停止了(可能是意外停止)
- job使用了
@DisallowConcurrentExecution注解
, job不能并发执行, 当达到下一个job执行点的时候, 上一个任务还没有完成 - job指定了过去了开始执行时, 例如当前时间为8点(am), 指定时间为7点(am)
3.2.2 有哪些策略
默认策略是MISFIRE_INSTRUCTION_SMART_POLICY
智能策略。这个策略会根据触发器的类型和配置来自动选择最合适的处理方式。
- 对于SimpleTrigger,如果错过了触发时间,Quartz会尽快触发任务执行,但不会丢失任何触发次数。
- 对于CronTrigger,如果错过了触发时间,Quartz会将触发器安排到下一个合适的触发时间,并保留当前的重复计数。
Quartz misfire详解
全网最好的一篇讲解Quartz的MisFire机制
4.SpringBoot整合Quartz
Quartz存储任务信息有两种方式,使用内存或者使用数据库来存储.
如果使用内存存储调度数据, 如果应用程序意外终止或者重启, 内存中的调度数据会丢失, 导致调度器失去对作业(job)和触发器的追踪, 从而导致任务丢失或不正确执行
通过将Quartz与Mysql集成, 可以将调度数据存储到数据库中, 从而实现数据的持久化.
- SpringBoot版本: 2.0.9.RELEASE
- Mysql版本: 8.0.26(因为我本地mysql是8.0版本)
4.1 数据库表准备
springboot整合quartz(集群环境)
4.2 Maven主要依赖
- SpringBoot整合quartz的启动器
- mysql连接驱动
- druid连接池(可选)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
这里使用 druid 作为数据库连接池,Quartz 默认使用 c3p0
4.3 quartz.properties
默认情况下, Quartz会加载classpath下的quartz.properties 作为配置文件, 如果找不到,则会使用 quartz 框架自己 jar 包下 org/quartz 底下的 quartz.properties 文件
#主要分为scheduler、threadPool、jobStore、dataSource等部分
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.instanceName=DefaultQuartzScheduler
#如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则将“rmi.export”标志设置为true
#在同一个配置文件中为'org.quartz.scheduler.rmi.export'和'org.quartz.scheduler.rmi.proxy'指定一个'true'值是没有意义的,如果你这样做'export'选项将被忽略
org.quartz.scheduler.rmi.export=false
#如果要连接(使用)远程服务的调度程序,则将“org.quartz.scheduler.rmi.proxy”标志设置为true。您还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099
org.quartz.scheduler.rmi.proxy=false
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
#实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
#threadCount和threadPriority将以setter的形式注入ThreadPool实例
#并发个数 如果你只有几个工作每天触发几次 那么1个线程就可以,如果你有成千上万的工作,每分钟都有很多工作 那么久需要50-100之间.
#只有1到100之间的数字是非常实用的
org.quartz.threadPool.threadCount=5
#优先级 默认值为5
org.quartz.threadPool.threadPriority=5
#可以是“true”或“false”,默认为false
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
#在被认为“misfired”(失火)之前,调度程序将“tolerate(容忍)”一个Triggers(触发器)将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)
org.quartz.jobStore.misfireThreshold=5000
# 默认存储在内存中,RAMJobStore快速轻便,但是当进程终止时,所有调度信息都会丢失
#org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
#持久化方式,默认存储在内存中,此处使用数据库方式
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作
# StdJDBCDelegate是一个使用“vanilla”JDBC代码(和SQL语句)来执行其工作的委托,用于完全符合JDBC的驱动程序
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#可以将“org.quartz.jobStore.useProperties”配置参数设置为“true”(默认为false),以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,
#因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题
org.quartz.jobStore.useProperties=true
#表前缀
org.quartz.jobStore.tablePrefix=QRTZ_
- scheduler属性
- org.quartz.scheduler.instanceName=DefaultQuartzScheduler: 设置Quartz调度器的实例名称, 在项目中通常只需要一个Quartz调度器实例, 一个调度器实例可以满足大多数情况下的需求
- org.quartz.scheduler.instanceId=AUTO: 设置Quartz调度器的实例标识符, 当设置为Auto时, 会自动为调度器分配一个唯一的实例标识符, 以确保在多个调度器实例运行时它们彼此不冲突
- jobDataMap属性
- org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX: 持久化方式,默认存储在内存中,此处使用数据库方式, 通常需要配合其他相关的配置属性,比如数据库连接信息、事务管理器等,以便Quartz能够正确地初始化并使用作业存储。
- org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate: 表示使用标准的JDBC驱动委托类。这个委托类适用于大多数常见的关系型数据库,比如MySQL、Oracle等, 配置这个属性时,需要确保选择的驱动委托类与所使用的数据库类型匹配,以便Quartz能够正确地生成和执行相应的SQL语句。
- org.quartz.jobStore.useProperties=true: 设置jobDataMap存储类型为字符串, 默认为false. 设置为true可以提高作业数据的可读性和可操作性,但需要确保存储的对象是可序列化的。
- org.quartz.jobStore.tablePrefix=QRTZ_: 表的前缀
- org.quartz.jobStore.isClustered=false: 是否加入集群, 默认为false
- org.quartz.jobStore.clusterCheckinInterval=5000: 集群环境下, 定期检查各个节点的时间间隔(毫秒)
- org.quartz.jobStore.txIsolationLevelReadCommitted=true: 设置存储的事务隔离级别
- ThreadPool属性
- org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool: 指定使用的线程池实现类, SimpleThreadPool表示使用简单的线程池, 这个简单线程池实现会根据配置的线程数量创建一组固定大小的线程池,在需要时分配线程给触发器执行任务。
- org.quartz.threadPool.threadCount=5: 调度器可以并发执行的任务数量,也就是同时执行的触发器任务数量。通过合理设置线程数量,可以控制应用的并发性能和资源利用率。
- org.quartz.threadPool.threadPriority=5: 属性用于指定Quartz调度器线程池中线程的优先级。
关于配置详细解释: Quartz系统参数配置详解
官网: 详细配置
4.4 初始化Scheduler, 并交给IOC容器管理
@Configuration
public class QuartzConfig implements SchedulerFactoryBeanCustomizer {
@Autowired
private DataSource dataSource;
@Override
public void customize(SchedulerFactoryBean schedulerFactoryBean) {
schedulerFactoryBean.setStartupDelay(2);
schedulerFactoryBean.setAutoStartup(true);
schedulerFactoryBean.setOverwriteExistingJobs(true);
}
@Bean
public Properties properties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
// 对quartz.properties文件进行读取
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
// 在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setQuartzProperties(properties());
schedulerFactoryBean.setDataSource(dataSource);
return schedulerFactoryBean;
}
/*
* 通过SchedulerFactoryBean获取Scheduler的实例
*/
@Bean
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
}
4.5 job任务类
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class JobOne extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("TimeEventJob正在执行..." + LocalDateTime.now());
// 执行10秒
try {
Thread.sleep(9000);
System.out.println("TimeEventJob执行完毕..." + LocalDateTime.now());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
4.6 实体类
public class JobInfo {
/**
* 任务名称
*/
private String jobName;
/**
* 任务组
*/
private String jobGroup;
/**
* 触发器名称
*/
private String triggerName;
/**
* 触发器组
*/
private String triggerGroup;
/**
* cron表达式
*/
private String cron;
/**
* 类名
*/
private String className;
/**
* 状态
*/
private String status;
/**
* 下一次执行时间
*/
private String nextTime;
/**
* 上一次执行时间
*/
private String prevTime;
/**
* 配置信息(data)
*/
private String config;
......
}
4.7 sevice
public class JobServicer {
@Resource
private Scheduler scheduler;
/**
* 添加任务
*/
@SuppressWarnings("unchecked")
public void addJob(JobInfo jobInfo) throws SchedulerException, ClassNotFoundException {
Objects.requireNonNull(jobInfo, "任务信息不能为空");
// 生成job key
JobKey jobKey = JobKey.jobKey(jobInfo.getJobName(), jobInfo.getJobGroup());
// 当前任务不存在才进行添加
if (!scheduler.checkExists(jobKey)) {
Class<Job> jobClass = (Class<Job>)Class.forName(jobInfo.getClassName());
// 任务明细
JobDetail jobDetail = JobBuilder
.newJob(jobClass)
.withIdentity(jobKey)
.withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup())
.withDescription(jobInfo.getJobName())
.build();
// 配置信息
jobDetail.getJobDataMap().put("config", jobInfo.getConfig());
// 定义触发器
TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getTriggerName(), jobInfo.getTriggerGroup());
// 设置任务的错过机制
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCron()).withMisfireHandlingInstructionDoNothing())
.build();
scheduler.scheduleJob(jobDetail, trigger);
} else {
throw new SchedulerException(jobInfo.getJobName() + "任务已存在,无需重复添加");
}
}
/**
* 任务暂停
*/
public void pauseJob(String jobGroup, String jobName) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
if (scheduler.checkExists(jobKey)) {
scheduler.pauseJob(jobKey);
}
}
/**
* 继续任务
*/
public void continueJob(String jobGroup, String jobName) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
if (scheduler.checkExists(jobKey)) {
scheduler.resumeJob(jobKey);
}
}
/**
* 删除任务
*/
public boolean deleteJob(String jobGroup, String jobName) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
if (scheduler.checkExists(jobKey)) {
// 这里还需要先删除trigger相关
//TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getTriggerName(), jobInfo.getTriggerGroup());
//scheduler.getTrigger()
//scheduler.rescheduleJob()
return scheduler.deleteJob(jobKey);
}
return false;
}
/**
* 获取任务信息
*/
public JobInfo getJobInfo(String jobGroup, String jobName) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
if (!scheduler.checkExists(jobKey)) {
return null;
}
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
if (Objects.isNull(triggers)) {
throw new SchedulerException("未获取到触发器信息");
}
TriggerKey triggerKey = triggers.get(0).getKey();
Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
JobInfo jobInfo = new JobInfo();
jobInfo.setJobName(jobGroup);
jobInfo.setJobGroup(jobName);
jobInfo.setTriggerName(triggerKey.getName());
jobInfo.setTriggerGroup(triggerKey.getGroup());
jobInfo.setClassName(jobDetail.getJobClass().getName());
jobInfo.setStatus(triggerState.toString());
if (Objects.nonNull(jobDetail.getJobDataMap())) {
jobInfo.setConfig(JSONObject.toJSONString(jobDetail.getJobDataMap()));
}
CronTrigger theTrigger = (CronTrigger) triggers.get(0);
jobInfo.setCron(theTrigger.getCronExpression());
return jobInfo;
}
}
4.8 controller
package com.itheima.controller;
import com.itheima.handler.JobHandler;
import com.itheima.pojo.JobInfo;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@RestController
@RequestMapping("/job")
public class QuartzController {
@Resource
private JobServicer jobHandler;
@Resource
private Scheduler scheduler;
/**
* 查询所有的任务
*/
@RequestMapping("/all")
public List<JobInfo> list() throws SchedulerException {
List<JobInfo> jobInfos = new ArrayList<>();
List<String> triggerGroupNames = scheduler.getTriggerGroupNames();
for (String triggerGroupName : triggerGroupNames) {
Set<TriggerKey> triggerKeySet = scheduler
.getTriggerKeys(GroupMatcher.triggerGroupEquals(triggerGroupName));
for (TriggerKey triggerKey : triggerKeySet) {
Trigger trigger = scheduler.getTrigger(triggerKey);
JobKey jobKey = trigger.getJobKey();
JobInfo jobInfo = jobHandler.getJobInfo(jobKey.getGroup(), jobKey.getName());
jobInfos.add(jobInfo);
}
}
return jobInfos;
}
/**
* 添加任务
*/
@PostMapping("/add")
public JobInfo addJob(@RequestBody JobInfo jobInfo) throws SchedulerException, ClassNotFoundException {
jobHandler.addJob(jobInfo);
return jobInfo;
}
/**
* 暂停任务
*/
@RequestMapping("/pause")
public void pauseJob(@RequestParam("jobGroup") String jobGroup, @RequestParam("jobName") String jobName)
throws SchedulerException {
jobHandler.pauseJob(jobGroup, jobName);
}
/**
* 继续任务
*/
@RequestMapping("/continue")
public void continueJob(@RequestParam("jobGroup") String jobGroup, @RequestParam("jobName") String jobName)
throws SchedulerException {
jobHandler.continueJob(jobGroup, jobName);
}
/**
* 删除任务
*/
@RequestMapping("/delete")
public boolean deleteJob(@RequestParam("jobGroup") String jobGroup, @RequestParam("jobName") String jobName)
throws SchedulerException {
return jobHandler.deleteJob(jobGroup, jobName);
}
}