定时调度框架Quartz使用

news/2025/1/15 6:53:32/文章来源:https://www.cnblogs.com/awqaear-blog/p/18518804

使用背景

在最近的项目中遇到一个需要使用到动态定时任务的需求,即定时任务的调用时间不是在某个固定时间自动执行,而是由用户控制,并且需要持久化。因此在网上搜了一下,发现了一个基于Java开发的Quartz定时任务调度框架,很符合我的需求,因此记录一下便于以后再次使用。

Quartz相关概念

quartz的使用过程主要涉及到以下几个概念:

1. Job

Job 是一个抽象接口,用户可以实现该接口,并且重写execute方法,在execute方法内,是用户需要实现的具体的功能

2. JobDetail

JobDetail 是对于Job的描述

3. JobDataMap

JobDataMap 是一个Map对象,用于存放Job需要的参数

4. Trigger

Trigger 用于触发定时任务

5. Scheduler

Scheduler 用于调度 JobDetail 和 Trigger

配置

首先创建了一个基础的SpringBoot项目(虽然Quartz不需要依赖SpringBoot也可以运行)
Maven依赖如下:

点击查看代码
    <dependencies><!--    SpringBoot依赖    --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.7.18</version></dependency><!--    SpringBootTest依赖    --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.7.18</version></dependency><!--    Quartz依赖    --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId><version>2.7.18</version></dependency><!--    数据库依赖,用于持久化定时任务    --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.3.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId><version>2.7.18</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.18</version></dependency><!--    Lombok    --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version></dependency></dependencies>

项目结构如下

项目结构

在项目启动后,SpringBoot已经维护了一个Scheduler

下面将以预售商品自动到期自动下架作为例子,使用Quartz框架

1. 创建并且调度任务


在 CommodityAutoOfflineJob 类中实现了Job接口,并且模拟下架商品的逻辑

点击查看代码
/*** 商品自动下架定时任务** @author panlijun* @since 2024/10/31 21:14*/
@Slf4j
public class AutoOfflineCommodityJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {log.info("开始执行商品自动下架定时任务");try {Thread.sleep(5_000L);} catch (InterruptedException e) {log.error("商品自动下架定时任务执行失败", e);throw new RuntimeException(e);}log.info("商品自动下架定时任务执行完毕");}
}

在 TestController 中创建了JobDetail 、Trigger 并且使用Scheduler对任务进行调度

点击查看代码
/*** @author panlijun* @since 2024/10/31 21:26*/
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {@Resourceprivate Scheduler scheduler;@SneakyThrows@RequestMapping("/creatAutoOfflineCommodityJob")public String test(@RequestParam String endTime) {log.info("endTime:{}",endTime);log.info("创建JobDetail");JobDetail jobDetail = JobBuilder.newJob(AutoOfflineCommodityJob.class).withIdentity("autoOfflineCommodityJob", "Commodity").build();log.info("创建Trigger");SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");Trigger trigger = TriggerBuilder.newTrigger().withIdentity("autoOfflineCommodityTrigger", "Commodity").startAt(format.parse(endTime)).build();log.info("调度任务");scheduler.scheduleJob(jobDetail, trigger);return "定时任务将于" + endTime + "执行";}
}

2. 使用自定义参数

在一些业务中,执行定时任务需要依靠一些具体的参数才能执行,上面的代码就不能满足需要了,因此对代码进行修改如下:

在Controller中额外接受要需要下架的商品id

public String test(@RequestParam String endTime, @RequestParam Integer commodityId)

创建JobDataMap对象

log.info("创建JobDataMap");
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("COMMODITY_ID", commodityId);

将接收到的商品id放入Map,并在创建JobDetail时作为参数传入

log.info("创建JobDetail");
JobDetail jobDetail = JobBuilder.newJob(AutoOfflineCommodityJob.class)
.withIdentity("autoOfflineCommodityJob", "Commodity")
.usingJobData(jobDataMap)
.build();

点击查看代码
    @SneakyThrows@RequestMapping("/creatAutoOfflineCommodityJob")public String test(@RequestParam String endTime,@RequestParam Integer commodityId) {log.info("endTime:{}", endTime);log.info("创建JobDataMap");JobDataMap jobDataMap = new JobDataMap();jobDataMap.put("COMMODITY_ID", commodityId);log.info("创建JobDetail");JobDetail jobDetail = JobBuilder.newJob(AutoOfflineCommodityJob.class).withIdentity("autoOfflineCommodityJob", "Commodity").usingJobData(jobDataMap).build();log.info("创建Trigger");SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");Trigger trigger = TriggerBuilder.newTrigger().withIdentity("autoOfflineCommodityTrigger", "Commodity").startAt(format.parse(endTime)).build();log.info("调度任务");scheduler.scheduleJob(jobDetail, trigger);return "定时任务将于" + endTime + "执行";}

并在定时任务中获取传入的参数

JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
Integer commodityId = (Integer) jobDataMap.get("COMMODITY_ID");

点击查看代码
    @Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {log.info("开始执行商品自动下架定时任务");try {JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();Integer commodityId = (Integer) jobDataMap.get("COMMODITY_ID");log.info("要下架的商品ID为:{}", commodityId);Thread.sleep(5_000L);} catch (InterruptedException e) {log.error("商品自动下架定时任务执行失败", e);throw new RuntimeException(e);}log.info("商品自动下架定时任务执行完毕");}

定时任务执行结果如下:


3. 定时任务持久化

Quartz默认将定时任务的数据保存在内存,每次系统重启都会丢失待运行的定时任务,这显然是不能接受的,因此需要对定时任务进行持久化,好在Quartz提供了对定时任务持久化的方法。

3.1 创建相关数据库表结构

以Mysql为例,需要创建以下表:

创建的表的SQL脚本可以在Quartz的代码仓库中找到,链接如下

https://github.com/quartz-scheduler/quartz/releases

SQL脚本就在下载文件的 quartz-2.3.2\quartz-core\src\main\resources\org\quartz\impl\jdbcjobstore 文件夹下

该文件夹下有很多种类的数据库的脚本,上图中框出的文件是MySQL的脚本

3.2 在项目中添加配置数据库和Quartz相关配置

点击查看代码
spring:application:name: quartz-study# 数据库配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/spring_task_test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=falseusername: rootpassword: 123456# quartz 持久化配置quartz:# 默认为memory,选择jdbc时,可以选择将定时任务持久化到数据库job-store-type: jdbcjdbc:# 每次启动项目时是否初始化表, 建议设置为never, 并且手动运行SQL脚本, 初始化数据库initialize-schema: never

3.3 修改代码,持久化定时任务

在创建JobDetail时设置.storeDurably(true),就能把定时任务持久化到数据库

点击查看代码
        log.info("创建JobDetail");JobDetail jobDetail = JobBuilder.newJob(AutoOfflineCommodityJob.class).withIdentity("autoOfflineCommodityJob", "Commodity").storeDurably(true).usingJobData(jobDataMap).build();

再次重启项目并调用接口

并查看数据库,发现数据库中增加了一条JobDetail数据

4. 恢复中断定时任务

在定时任务运行过程中,进程被终止了,在重启项目后,是不会重新执行被中断的定时任务的

如果需要恢复运行中被中断的定时任务,只需要设置 .requestRecovery(true) 就可以在重启时重新执行被中断的定时任务

点击查看代码
        log.info("创建JobDetail");JobDetail jobDetail = JobBuilder.newJob(AutoOfflineCommodityJob.class).withIdentity("autoOfflineCommodityJob", "commodity").usingJobData(jobDataMap).requestRecovery(true).storeDurably(true).build();

注意事项

在Quartz中,JobDetail .withIdentity("autoOfflineCommodityJob", "commodity")中的两个参数也就是namegroup构成的二元组是不可以重复,在以下情况

  1. 不使用持久化定时任务时,上一个同名 JobDetail 还未完成

  2. 使用持久化定时任务时,创建同名定时任务,即使上一个定时任务已经完成

会出现无法创建定时任务的情况

因此,如果可以复用JobDetail,则尽量复用,当无法复用JobDetail时,则需要给JobDetail不同的namegroup

点击查看代码
        // 根据雪花算法生成IDSnowflake snowflake = IdUtil.getSnowflake();log.info("创建JobDetail");JobDetail jobDetail = JobBuilder.newJob(AutoOfflineCommodityJob.class).withIdentity(snowflake.nextIdStr() + "_Job", "commodity").usingJobData(jobDataMap).requestRecovery(true).storeDurably(true).build();log.info("创建Trigger");SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");Trigger trigger = TriggerBuilder.newTrigger().forJob(jobDetail).withIdentity(snowflake.nextIdStr() + "_Trigger", "Commodity").startAt(format.parse(endTime)).build();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/824932.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络重置后WiFi列表消失

打开服务 启动WLAN AutoConfig

当然不是草台班子 云译网 原型设计+概要设计

作业所属课程 软件工程2024作业要求 2024秋软工实践团队作业-第二次作业目标 设计出原型与后端架构团队名称 当然不是草台班子团队成员学号 姓名102201427 侯丽珂102201426 郑嘉祺102201241 戴康怡102201218 肖晗涵112200328 谢李东292300304 陈鹭102201242 魏儀阳082100170 朱…

【入门笔记】CSE 365 - Fall 2024之Intercepting Communication(pwn.college)

从构造发送数据包让你更加理解数据包的构成【入门笔记】CSE 365 - Fall 2024之Intercepting Communication(pwn.college) level1 连接到远程主机查看解析 为了知道目标远程主机的ip地址,我们运行`/challenge/run`开启远程主机环境 再使用nc连接到远程主机的特定端口 level…

竹杖芒鞋数模路

“人终将被年少不可得之物困扰一生”——对于我来说,我的年少不可得之物,便是这三个字:“集训队”。当初我来到学校,听说到数模集训队、ACM集训队的时候,我特别特别非常兴奋激动,让我想起了往昔对“集训队”这三个字的无限向往,“集训队”这三个字就是那么吸引我。“人终…

跟着禹神学前端——CSS 基础 (2)

1. CSS 长度单位px:像素 em:相对元素 font-size 的倍数 rem:相对根字体大小的倍数,html 标签就是根。 %:相对父元素计算的百分比。CSS中设置长度,必须加单位,否则样式无效。2. 元素的显示模式块元素(block)又称:块级元素 特点:在页面中独占一行,不会与任何元素共用…

网易云课堂上买的课程过期了怎么办?教你如何下载到本地永久观看~

前言:很多同学都遇到过购买的网课课程过期了,然后无法观看,花了钱还没学完,血亏。这里教大家一种方法,把网易云课堂上面快过期的课程下载到电脑本地,然后可以永久观看,再也不用担心过期了~ 【已经过期的课程也是支持下载的哦!】 提示:操此方法需要用到Windows电脑,Ma…

跟着禹神学前端——CSS 基础 (1)

CSS (Cascading Style Sheets,层叠样式表),是一种用来为结构化文档(如 HTML 文档或 XML 应用)添加样式(字体、间距和颜色等)的计算机语言,CSS 文件扩展名为 .css1. CSS 简介CSS 的全称为:层叠样式表(Cascading Style Sheets)。 CSS 也是一种标记语言。用于给 HTML 结…

xFormers pip 安装

最近复现的一些仓库用 xFormers 的很多,在 arm 的 aarch64 下安装所有和 CUDA 相关的库都不是非常方便,这里记录一下。 参考: https://github.com/facebookresearch/xformers https://blog.csdn.net/x1131230123/article/details/139231686 首先要确定版本,xFormers 高度绑…

Vue3 + Vue Cli 搭建项目(详细)

搭建:Vue CLI 5 + Vue 3 + Ant Design Vue 3Vue CLI 5:是vue + 大量的第三方组件; Vue 3:是页面开发基于Vue; Ant Design Vue 3:是基于Vue3的UI组件;关于UI还有CSS的Bootstrap。 1. 本地环境准备 按照NodeJS得到npm,使用npm安装 vue cli(脚手架),使用vue cli创建项目…

[BUG]Cursor Chat功能一直在转圈但是不输出信息

问题描述 时间: 20241031 表现在cursor中输入问题后使用 chat功能, 并不能得到有效回答 cursor版本:解决方案: 使用cursor中的Edit功能绕过chat方法: 我不清楚为什么生效, 但是按照下面这种路径操作确实是解决了问题我是darkchink, 如果有其他疑问或者有想要交流的朋友欢迎加我…

概率论沉思录:初等抽样论

我们先考察无放回抽样(sampling without replacement) 实验,也即从有N个球的坛子里无放回地抽n个球,我们会发现实验结果服从超几何分布/广义超几何分布。接着,我们会讨论前向推断和后向推断两类问题。然后,我们会研究无放回抽样的极限形式,这将导出二项分布/多项分布。关…

后台业务系统OA,CRM,ERP,HR这类开发用什么前端UI框架更好

在开发后台业务系统如OA、CRM、ERP和HR时,选择合适的前端UI框架至关重要。本文主要探讨:一、Bootstrap框架;二、Ant Design框架;三、Element UI框架;四、Vue.js框架;五、React框架。考虑到这些系统的复杂性和对界面的要求,以下框架能够提供高效、统一和用户友好的体验。…