复习Quartz 框架
1.Quartz 框架简介
Quartz 是一个功能强大的开源任务调度框架,允许开发人员在 Java 应用程序中设置复杂的任务计划。它可以根据指定的时间表运行任务,支持简单的时间间隔、复杂的日历规则和分布式环境。
特性
- 灵活的任务调度:支持简单和复杂的调度规则。
- 持久性支持:通过数据库存储任务数据。
- 分布式任务调度:支持集群模式。
- 多种触发器:简单触发器(SimpleTrigger)和基于 Cron 表达式的触发器(CronTrigger)。
- 线程池管理:优化资源使用。
- 易于集成:可以与 Spring 和其他框架无缝集成。
2.Quartz 的核心组件
- Scheduler(调度器):Quartz 的主控类,负责管理任务的调度。
- Job(任务):实现特定逻辑的任务类,需实现 Job 接口。
- JobDetail(任务详情):定义任务实例及其属性。
- Trigger(触发器):定义任务的触发规则,包括 SimpleTrigger 和 CronTrigger。
- JobStore(任务存储):用于任务的持久化存储。
3. Quartz 使用步骤
1.引入依赖
<!-- Maven依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2.配置YML
quartz:
job-store-type: jdbc # 使用 JDBC 存储作业信息
jdbc:
initialize-schema: never # 不自动初始化数据库表架构 (第一次启动用always)
properties:
org:
quartz:
# scheduler:
# instanceName: quartzScheduler # Quartz Scheduler 实例名称
# instanceId: AUTO
threadPool:
threadCount: 10 # Quartz 线程池中线程的数量
jobStore:
misfireThreshold: 60000 # 错过触发的任务处理时间阈值(毫秒)
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore # 使用JDBC JobStore
useProperties: false # 是否使用属性存储数据
tablePrefix: QRTZ_
3.配置JOB
package com.example.lx.quartz;
import com.alibaba.fastjson2.JSONObject;
import com.example.lx.demos.entity.Area;
import com.example.lx.demos.mapper.AreaMapper;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author lx
* @date 2024/11/23
* @description
*/
@Component
@Slf4j
public class MyBusinessJob implements Job {
@Autowired
private AreaMapper areaMapper;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 获取任务的自定义参数
String param = (String) context.getJobDetail().getJobDataMap().get("param");
QuartzDTO dto = JSONObject.parseObject(param, QuartzDTO.class);
// try {
// log.info("=========================开始睡眠====================");
// System.out.println("睡眠前时间:" + System.currentTimeMillis());
// Thread.sleep(8000);
// System.out.println("睡眠后时间:" + System.currentTimeMillis());
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// 写入你的业务逻辑
// 假设任务逻辑是打印时间
Area area = areaMapper.selectById(1);
System.out.println(area.getAreaName());
log.info("=========================业务逻辑====================");
System.out.println("当前时间:" + System.currentTimeMillis());
System.out.println("执行业务任务,参数为:" + param);
log.info("jobName:{}", context.getJobDetail().getKey().getName());
log.info("jobGroup:{}", context.getJobDetail().getKey().getGroup());
log.info("triggerName:{}", context.getTrigger().getKey().getName());
log.info("triggerGroup:{}", context.getTrigger().getKey().getGroup());
log.info("上次触发时间:{}", context.getPreviousFireTime());
log.info("本次触发时间:{}", context.getFireTime());
log.info("下次触发时间:{}", context.getNextFireTime());
log.info("调度时间:{}", context.getScheduledFireTime());
log.info(String.format("事件间隔%s分钟,表达式为:%s",dto.getIntervalMinutes(),dto.getCronExpression()));
}
}
4.配置CRUD
package com.zowe.operateplatform.quartz.service;
import com.alibaba.fastjson.JSON;
import com.zowe.operateplatform.dao.WarnMapper;
import com.zowe.operateplatform.entity.Warn;
import com.zowe.operateplatform.quartz.QuartzPO;
import com.zowe.operateplatform.quartz.constant.QuartzConstants;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* @author lx
* @date 2024/11/26
* @description
*/
@Service
@Slf4j
public class QuartzTaskService {
@Autowired
private Scheduler scheduler;
@Autowired
private WarnMapper warnMapper;
/**
* 获取 JobDetail
* @param po QuartzPO 包含任务名、组名等信息
* @param jobClass 任务类
* @param triggerSuffix 触发器后缀区分 Cron 或 Simple
*/
public JobDetail getJobDetail(QuartzPO po, Class<? extends Job> jobClass, String triggerSuffix) throws SchedulerException {
String jobName = po.getJobName();
String warning = JSON.toJSONString(po);
JobKey jobKey = JobKey.jobKey(jobName + triggerSuffix, triggerSuffix);
if (scheduler.checkExists(jobKey)) {
throw new RuntimeException("任务已存在:" + jobKey);
}
// 创建 JobDetail
return JobBuilder.newJob(jobClass)
.withIdentity(jobKey)
.usingJobData("param", warning) // 添加自定义参数
.build();
}
/**
* 添加任务(通用方法)
* @param jobDetail JobDetail 实例
* @param trigger 触发器
*/
private void scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException {
scheduler.scheduleJob(jobDetail, trigger);
log.info("任务已添加:{}", jobDetail.getKey());
}
/**
* 添加 Cron 任务
*/
public void addCronJob(QuartzPO po, Class<? extends Job> jobClass) throws SchedulerException {
JobDetail jobDetail = getJobDetail(po, jobClass, QuartzConstants.CRON_JOB_NAME_SUFFIX);
String cronExpression = po.getCronExpression();
// 校验 Cron 表达式合法性
if (!CronExpression.isValidExpression(cronExpression)) {
throw new IllegalArgumentException("无效的 Cron 表达式:" + cronExpression);
}
// 创建 CronTrigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobDetail.getKey().getName(), QuartzConstants.CRON_GROUP_NAME)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.build();
scheduleJob(jobDetail, trigger);
}
/**
* 添加 Simple 任务
*/
public void addSimpleJob(QuartzPO po, Class<? extends Job> jobClass) throws SchedulerException {
JobDetail jobDetail = getJobDetail(po, jobClass, QuartzConstants.SIMPLE_JOB_NAME_SUFFIX);
Integer intervalMinutes = po.getIntervalMinutes();
if (intervalMinutes == null || intervalMinutes <= 0) {
throw new IllegalArgumentException("间隔时间必须大于 0 分钟");
}
// 创建 SimpleTrigger
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobDetail.getKey().getName(), QuartzConstants.SIMPLE_GROUP_NAME)
.startAt(new Date()) // 立即开始
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMinutes(intervalMinutes) // 间隔
.repeatForever()) // 无限循环
.build();
scheduleJob(jobDetail, trigger);
}
/**
* 删除任务
*/
public void deleteJob(String jobName, String groupName) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(jobName, groupName);
if (!scheduler.checkExists(jobKey)) {
log.warn("任务不存在:{}", jobKey);
return;
}
scheduler.deleteJob(jobKey);
log.info("任务已删除:{}", jobKey);
}
/**
* 暂停任务
*/
public boolean pauseJob(Long id,String jobName, String groupName) {
Warn warn = warnMapper.selectById(id);
if (warn == null) {
return false;
}
if (warn.getStatus()==1) {
warn.setStatus(2);
warnMapper.updateById(warn);
}else {
return false;
}
JobKey jobKey = JobKey.jobKey(jobName, groupName);
try {
if (!scheduler.checkExists(jobKey)) {
log.warn("任务不存在:{}", jobKey);
return false;
}
scheduler.pauseJob(jobKey);
log.info("任务已暂停:{}", jobKey);
return true;
}catch (SchedulerException e){
return false;
}
}
/**
* 恢复任务
*/
public boolean resumeJob(Long id,String jobName, String groupName) {
// 更新warn表状态
Warn warn = warnMapper.selectById(id);
if (warn == null) {
return false;
}
if (warn.getStatus()==2) {
warn.setStatus(1);
warnMapper.updateById(warn);
}else {
return false;
}
JobKey jobKey = JobKey.jobKey(jobName, groupName);
try {
if (!scheduler.checkExists(jobKey)) {
log.warn("任务不存在:{}", jobKey);
return false;
}
scheduler.resumeJob(jobKey);
log.info("任务已恢复:{}", jobKey);
return true;
}catch (SchedulerException e){
return false;
}
}
/**
* 更新任务(仅支持 Cron 任务)
*/
// public void updateCronJob(QuartzPO po, Class<? extends Job> jobClass) throws SchedulerException {
// String jobName = po.getJobName();
// String groupName = po.getGroupName();
// String newCronExpression = po.getCronExpression();
// JobDetail jobDetail = getJobDetail(po, jobClass, QuartzConstants.SIMPLE_JOB_NAME_SUFFIX);
// // 校验 Cron 表达式合法性
// if (!CronExpression.isValidExpression(newCronExpression)) {
// throw new IllegalArgumentException("无效的 Cron 表达式:" + newCronExpression);
// }
//
// JobKey jobKey = JobKey.jobKey(jobName + QuartzConstants.CRON_JOB_NAME_SUFFIX, groupName);
//
// if (!scheduler.checkExists(jobKey)) {
// log.warn("任务不存在,无法更新:{}", jobKey);
// return;
// }
//
// // 删除旧触发器并添加新触发器
// TriggerKey triggerKey = TriggerKey.triggerKey(jobName + QuartzConstants.CRON_JOB_NAME_SUFFIX, groupName);
// scheduler.pauseTrigger(triggerKey);
// scheduler.unscheduleJob(triggerKey);
// triggerKey = TriggerKey.triggerKey(jobName +jobName+QuartzConstants.CRON_JOB_NAME_SUFFIX, groupName);
// CronTrigger newTrigger = TriggerBuilder.newTrigger()
// .withIdentity(triggerKey)
// .forJob(jobKey)
// .withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
// .build();
//
// scheduler.scheduleJob(jobDetail,newTrigger);
// log.info("任务已更新:{}", jobKey);
// }
}
4.Quartz 调度器
在 Quartz 框架中,调度器(Scheduler) 是整个任务调度系统的核心组件,负责管理任务的生命周期,包括任务的创建、触发、暂停、恢复和删除。
1.调度器的核心职责
1. 任务管理:
注册、更新和删除任务。
2. 触发规则:
根据触发器定义的时间计划,决定何时执行任务。
3.线程管理:
通过线程池并发执行任务。
4. 持久化支持:
将任务信息存储到数据库,以支持任务的持久化和分布式部署。
5.状态管理:
- 启动:start()
- 暂停:pauseAll() 或 pauseJob()
- 恢复:resumeAll() 或 resumeJob()
- 停止:shutdown()
2.调度器的核心方法
方法 | 功能 |
---|---|
start() | 启动调度器,开始执行已注册的任务。 |
shutdown() | 停止调度器,不会触发任何任务,但不会中断正在执行的任务。 |
standby() | 将调度器置于待命状态,暂停触发任务。 |
pauseJob(JobKey key) | 暂停指定的任务。 |
pauseAll() | 暂停所有任务。 |
resumeJob(JobKey key) | 恢复指定的任务。 |
resumeAll() | 恢复所有任务。 |
deleteJob(JobKey key) | 删除指定任务。 |
scheduleJob(JobDetail, Trigger) | 注册任务及其触发器。 |
getJobDetail(JobKey key) | 获取任务的详细信息。 |
getTrigger(TriggerKey key) | 获取触发器的详细信息。 |
isStarted() | 检查调度器是否已启动。 |
5.日志
1.SQL
CREATE TABLE `quartz_task_logs` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`job_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务名称',
`job_group` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务组名',
`trigger_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '触发器名称',
`trigger_group` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '触发器组名',
`fire_time` datetime NOT NULL COMMENT '任务触发时间',
`finish_time` datetime NULL DEFAULT NULL COMMENT '任务完成时间',
`status` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务状态(SUCCESS/FAILURE)',
`exception_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '异常信息(如果有)',
`execution_time` bigint(20) NULL DEFAULT NULL COMMENT '执行耗时(毫秒)',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Quartz任务执行日志' ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
2.代码
@Component
public class QuartzJobListener implements JobListener {
@Autowired
private QuartzTaskLogsService quartzTaskLogsService; // 你的日志表存储仓库
@Override
public String getName() {
return "GlobalQuartzJobListener";
}
@Override
public void jobToBeExecuted(JobExecutionContext context) {
// 创建任务日志记录
QuartzTaskLogs log = new QuartzTaskLogs();
log.setJobName(context.getJobDetail().getKey().getName());
log.setJobGroup(context.getJobDetail().getKey().getGroup());
log.setTriggerName(context.getTrigger().getKey().getName());
log.setTriggerGroup(context.getTrigger().getKey().getGroup());
log.setFireTime(new Date()); // 任务开始时间
log.setStatus("RUNNING"); // 初始状态:运行中
// 保存日志到数据库
quartzTaskLogsService.save(log);
// 将日志 ID 存入上下文
context.put("logId", log.getId());
}
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
// 获取日志 ID
Long logId = (Long) context.get("logId");
if (logId == null) {
return; // 如果没有日志 ID,不更新记录
}
QuartzTaskLogs log = quartzTaskLogsService.getById(logId);
if (log == null) {
return; // 如果日志不存在,不更新记录
}
// 更新任务完成时间和状态
log.setFinishTime(new Date()); // 完成时间
log.setExecutionTime(System.currentTimeMillis() - log.getFireTime().getTime()); // 耗时
if (jobException == null) {
// 执行成功
log.setStatus("SUCCESS");
} else {
// 执行失败,记录错误信息
log.setStatus("FAILURE");
log.setExceptionMessage(jobException.getMessage());
}
// 保存更新后的日志
quartzTaskLogsService.updateById(log);
}
// 可选:处理被拒绝执行的任务
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
Long logId = (Long) context.get("logId");
if (logId == null) {
return; // 如果没有日志 ID,不更新记录
}
QuartzTaskLogs log = quartzTaskLogsService.getById(logId);
if (log == null) {
return; // 如果日志不存在,不更新记录
}
log.setStatus("VETOED"); // 标记为被拒绝
log.setFinishTime(new Date()); // 设置结束时间
// 保存日志
quartzTaskLogsService.updateById(log);
}
}