【 复习Quartz 框架】

1.Quartz 框架简介

Quartz 是一个功能强大的开源任务调度框架,允许开发人员在 Java 应用程序中设置复杂的任务计划。它可以根据指定的时间表运行任务,支持简单的时间间隔、复杂的日历规则和分布式环境。

特性

  1. 灵活的任务调度:支持简单和复杂的调度规则。
  2. 持久性支持:通过数据库存储任务数据。
  3. 分布式任务调度:支持集群模式。
  4. 多种触发器:简单触发器(SimpleTrigger)和基于 Cron 表达式的触发器(CronTrigger)。
  5. 线程池管理:优化资源使用。
  6. 易于集成:可以与 Spring 和其他框架无缝集成。

2.Quartz 的核心组件

  1. Scheduler(调度器):Quartz 的主控类,负责管理任务的调度。
  2. Job(任务):实现特定逻辑的任务类,需实现 Job 接口。
  3. JobDetail(任务详情):定义任务实例及其属性。
  4. Trigger(触发器):定义任务的触发规则,包括 SimpleTrigger 和 CronTrigger。
  5. 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);
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值