乐趣区

关于java:SpringBoot官方支持任务调度框架轻量级用起来也挺香

之前咱们讲过一个分布式任务调度框架 PowerJob,能够通过可视化的形式来进行任务调度。然而有时候咱们只是须要一个轻量级的任务调度性能,而 PowerJob 须要搭建调度核心未免有些重,这时候 SpringBoot 官网反对的任务调度框架 Quartz 就派上用场了!本文次要介绍 Quartz 在 SpringBoot 中的应用,让你在实现任务调度上有更多的抉择!

SpringBoot 实战电商我的项目 mall(40k+star)地址:https://github.com/macrozheng/mall

Quartz 简介

Quartz 是一款功能强大的开源任务调度框架,简直能够集成到任何 Java 应用程序中(小到单机利用,大到分布式应用)。Quartz 可用于创立简略或简单的任务调度,用以执行数以万计的工作。工作被定义为标准化的 Java 组件,Java 编写的工作都能够被执行。

外围概念

Quartz 中有一些比拟外围的概念,了解它们对应用 Quartz 很有帮忙!

  • Scheduler(调度器):Quartz 中的任务调度器,通过 Trigger 和 JobDetail 能够用来调度、暂停和删除工作。
  • Trigger(触发器):Quartz 中的触发器,能够通过 CRON 表达式来指定工作执行的工夫,工夫到了会主动触发工作执行。
  • JobDetail(工作详情):Quartz 中须要执行的工作详情,包含了工作的惟一标识和具体要执行的工作,能够通过 JobDataMap 往工作中传递数据。
  • Job(工作):Quartz 中具体的工作,蕴含了执行工作的具体方法。

CRON 表达式

Cron 表达式是一个字符串,包含 6~7 个工夫元素,在 Quartz 中能够用于指定工作的执行工夫。

CRON 的语法格局

Seconds Minutes Hours DayofMonth Month DayofWeek

CRON 格局中每个工夫元素的阐明

工夫元素 可呈现的字符 无效数值范畴
Seconds , – * / 0-59
Minutes , – * / 0-59
Hours , – * / 0-23
DayofMonth , – * / ? L W 0-31
Month , – * / 1-12
DayofWeek , – * / ? L # 1- 7 或 SUN-SAT

CRON 格局中特殊字符阐明

字符 作用 举例
, 列出枚举值 在 Minutes 域应用 5,10,示意在 5 分和 10 分各触发一次
\- 示意触发范畴 在 Minutes 域应用 5 -10,示意从 5 分到 10 分钟每分钟触发一次
\* 匹配任意值 在 Minutes 域应用 *, 示意每分钟都会触发一次
/ 起始工夫开始触发,每隔固定工夫触发一次 在 Minutes 域应用 5 /10, 示意 5 分时触发一次,每 10 分钟再触发一次
? 在 DayofMonth 和 DayofWeek 中,用于匹配任意值 在 DayofMonth 域应用?, 示意每天都触发一次
\# 在 DayofMonth 中,确定第几个星期几 1#3 示意第三个星期日
L 示意最初 在 DayofWeek 中应用 5L, 示意在最初一个星期四触发
W 示意无效工作日(周一到周五) 在 DayofMonth 应用 5W,如果 5 日是星期六,则将在最近的工作日 4 日触发一次

在线 CRON 表达式生成器

其实 CRON 表达式无需多记,须要应用的时候间接应用在线生成器就能够了,地址:https://cron.qqe2.com/

整合 SpringBoot 应用

接下来咱们讲下如何在 SpringBoot 中应用 Quartz 来实现任务调度,在电商零碎中往往会有须要定时发送邮件或者站内信的需要,咱们以此为场景来实现下!

  • Quartz 存储工作信息有两种形式,应用内存或者应用数据库来存储,这里咱们采纳数据库存储的形式,首先须要新建 Quartz 的相干表,建表脚本在我的项目的 resources 目录下,名称为tables_mysql.sql,创立胜利后数据库中多出 11 张表;

  • 接下来在 pom.xml 中增加 Quartz 的相干依赖即可,SpringBoot 官网曾经给咱们提供好了相干 Starter;
<!--SpringBoot 集成 QuartZ-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
  • application.yml 中增加 Quartz 相干配置,配置阐明间接看正文就好了,次要是对 schedulerjobStorethreadPool进行配置;
spring:
  quartz:
    job-store-type: jdbc # quartz 工作存储类型:jdbc 或 memory
    wait-for-jobs-to-complete-on-shutdown: true # 敞开时期待工作实现
    overwrite-existing-jobs: true # 能够笼罩已有的工作
    properties: # quartz 原生配置
      org:
        quartz:
          scheduler:
            instanceName: scheduler # 调度器实例名称
            instanceId: AUTO # 调度器实例 ID 主动生成
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX # 调度信息存储解决类
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 应用齐全兼容 JDBC 的驱动
            tablePrefix: QRTZ_ # quartz 相干表前缀
            useProperties: false # 是否将 JobDataMap 中的属性转为字符串存储
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool # 指定线程池实现类,对调度器提供固定大小的线程池
            threadCount: 10 # 设置并发线程数量
            threadPriority: 5 # 指定线程优先级
  • 创立任务调度业务接口,定义好三个办法,别离为通过 CRON 表达式调度工作、调度指定工夫的工作和勾销定时工作;
/**
 * Quartz 定时工作操作类
 * Created by macro on 2020/9/27.
 */
public interface ScheduleService {
    /**
     * 通过 CRON 表达式调度工作
     */
    String scheduleJob(Class<? extends Job> jobBeanClass, String cron, String data);

    /**
     * 调度指定工夫的工作
     */
    String scheduleFixTimeJob(Class<? extends Job> jobBeanClass, Date startTime, String data);

    /**
     * 勾销定时工作
     */
    Boolean cancelScheduleJob(String jobName);
}
  • 创立任务调度业务实现类,通过 SchedulerCronTriggerJobDetail 的 API 实现相干办法;
/**
 * Quartz 定时工作操作实现类
 * Created by macro on 2020/9/27.
 */
@Slf4j
@Service
public class ScheduleServiceImpl implements ScheduleService {
    @Autowired
    private Scheduler scheduler;
    private String defaultGroup = "default_group";

    @Override
    public String scheduleJob(Class<? extends Job> jobBeanClass, String cron, String data) {
        // 创立须要执行的工作
        String jobName = UUID.fastUUID().toString();
        JobDetail jobDetail = JobBuilder.newJob(jobBeanClass)
                .withIdentity(jobName, defaultGroup)
                .usingJobData("data", data)
                .build();
        // 创立触发器,指定工作执行工夫
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity(jobName, defaultGroup)
                .withSchedule(CronScheduleBuilder.cronSchedule(cron))
                .build();
        // 应用调度器进行任务调度
        try {scheduler.scheduleJob(jobDetail, cronTrigger);
        } catch (SchedulerException e) {e.printStackTrace();
            log.info("创立定时工作失败!");
        }
        return jobName;
    }

    @Override
    public String scheduleFixTimeJob(Class<? extends Job> jobBeanClass, Date startTime, String data) {
        // 日期转 CRON 表达式
        String startCron = String.format("%d %d %d %d %d ? %d",
                DateUtil.second(startTime),
                DateUtil.minute(startTime),
                DateUtil.hour(startTime, true),
                DateUtil.dayOfMonth(startTime),
                DateUtil.month(startTime) + 1,
                DateUtil.year(startTime));
        return scheduleJob(jobBeanClass, startCron, data);
    }

    @Override
    public Boolean cancelScheduleJob(String jobName) {
        boolean success = false;
        try {
            // 暂停触发器
            scheduler.pauseTrigger(new TriggerKey(jobName, defaultGroup));
            // 移除触发器中的工作
            scheduler.unscheduleJob(new TriggerKey(jobName, defaultGroup));
            // 删除工作
            scheduler.deleteJob(new JobKey(jobName, defaultGroup));
            success = true;
        } catch (SchedulerException e) {e.printStackTrace();
        }
        return success;
    }
}
  • 定义好须要执行的工作,继承 QuartzJobBean 类,实现 executeInternal 办法即可,这里定义了三个工作,定时发送邮件、定时发送站内信和执行 CRON 表达式工作;
/**
 * 发送邮件定时工作执行器
 * Created by macro on 2020/9/27.
 */
@Slf4j
@Component
public class SendEmailJob extends QuartzJobBean {
    @Autowired
    private ScheduleService scheduleService;

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {Trigger trigger = jobExecutionContext.getTrigger();
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        String data = jobDataMap.getString("data");
        log.info("定时发送邮件操作:{}",data);
        // 实现后删除触发器和工作
        scheduleService.cancelScheduleJob(trigger.getKey().getName());
    }
}
/**
 * 发送站内信定时工作执行器
 * Created by macro on 2020/9/27.
 */
@Slf4j
@Component
public class SendMessageJob extends QuartzJobBean {
    @Autowired
    private ScheduleService scheduleService;

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {Trigger trigger = jobExecutionContext.getTrigger();
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        String data = jobDataMap.getString("data");
        log.info("定时发送站内信操作:{}",data);
        // 实现后删除触发器和工作
        scheduleService.cancelScheduleJob(trigger.getKey().getName());
    }
}
/**
 * 应用 CRON 表达式的工作执行器
 * Created by macro on 2020/9/29.
 */
@Slf4j
@Component
public class CronProcessJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {JobDetail jobDetail = jobExecutionContext.getJobDetail();
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        String data = jobDataMap.getString("data");
        log.info("CRON 表达式工作执行:{}",data);
    }
}
  • 最初创立好任务调度相干接口,调用任务调度业务类即可。
/**
 * 定时任务调度相干接口
 * Created by macro on 2020/9/29.
 */
@Api(tags = "ScheduleController", description = "定时任务调度相干接口")
@RestController
@RequestMapping("/schedule")
public class ScheduleController {
    @Autowired
    private ScheduleService scheduleService;

    @ApiOperation("定时发送邮件")
    @PostMapping("/sendEmail")
    public CommonResult sendEmail(@RequestParam String startTime,@RequestParam String data) {Date date = DateUtil.parse(startTime, DatePattern.NORM_DATETIME_FORMAT);
        String jobName = scheduleService.scheduleFixTimeJob(SendEmailJob.class, date, data);
        return CommonResult.success(jobName);
    }

    @ApiOperation("定时发送站内信")
    @PostMapping("/sendMessage")
    public CommonResult sendMessage(@RequestParam String startTime,@RequestParam String data) {Date date = DateUtil.parse(startTime, DatePattern.NORM_DATETIME_FORMAT);
        String jobName = scheduleService.scheduleFixTimeJob(SendMessageJob.class, date, data);
        return CommonResult.success(jobName);
    }

    @ApiOperation("通过 CRON 表达式调度工作")
    @PostMapping("/scheduleJob")
    public CommonResult scheduleJob(@RequestParam String cron, @RequestParam String data) {String jobName = scheduleService.scheduleJob(CronProcessJob.class, cron, data);
        return CommonResult.success(jobName);
    }

    @ApiOperation("勾销定时工作")
    @PostMapping("/cancelScheduleJob")
    public CommonResult cancelScheduleJob(@RequestParam String jobName) {Boolean success = scheduleService.cancelScheduleJob(jobName);
        return CommonResult.success(success);
    }
}

运行测试

  • 调用定时发送邮件接口测试;

  • 到点之后发现控制台曾经打印工作执行信息;
2020-09-30 11:23:00.035  INFO 10160 --- [eduler_Worker-1] com.macro.mall.tiny.job.SendEmailJob     : 定时发送邮件操作:发送邮件内容
  • 应用 CRON 表达式来启动一个定时工作,从 0s 开始,每隔 10s 执行一次;

  • 控制台每隔 10s 打印工作执行信息;
2020-09-30 11:26:30.024  INFO 10160 --- [eduler_Worker-2] com.macro.mall.tiny.job.CronProcessJob   : CRON 表达式工作执行:CRON 音讯内容
2020-09-30 11:26:40.025  INFO 10160 --- [eduler_Worker-3] com.macro.mall.tiny.job.CronProcessJob   : CRON 表达式工作执行:CRON 音讯内容
2020-09-30 11:26:50.017  INFO 10160 --- [eduler_Worker-4] com.macro.mall.tiny.job.CronProcessJob   : CRON 表达式工作执行:CRON 音讯内容
2020-09-30 11:27:00.023  INFO 10160 --- [eduler_Worker-5] com.macro.mall.tiny.job.CronProcessJob   : CRON 表达式工作执行:CRON 音讯内容
2020-09-30 11:27:10.019  INFO 10160 --- [eduler_Worker-6] com.macro.mall.tiny.job.CronProcessJob   : CRON 表达式工作执行:CRON 音讯内容
  • 咱们能够通过启动工作返回的jobName,调用勾销定时工作的接口来勾销工作,调用胜利后定时工作不在执行。

参考资料

官网文档:http://www.quartz-scheduler.o…

我的项目源码地址

https://github.com/macrozheng…

本文 GitHub https://github.com/macrozheng/mall-learning 曾经收录,欢送大家 Star!

退出移动版