Springboot 整合 Quartz(定时任务框架)

一、java 定时任务调度的实现方式

1、Timer

特点是:简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务;能实现简单的定时任务,稍微复杂点(或要求高一些)的定时任务却不好实现。

2、ScheduledExecutor

鉴于 Timer 的缺陷,Java 5 推出了基于线程池设计的 ScheduledExecutor;
特点:每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。需要注意的是,只有当任务的执行时间到来时,ScheduedExecutor 才会真正启动一个线程,其余时间 ScheduledExecutor 都是在轮询任务的状态。
虽然用 ScheduledExecutor 和 Calendar 能够实现复杂任务调度,但实现起来还是比较麻烦,对开发还是不够友善。

3、Spring Scheduler

Spring 对任务调度的实现支持,可以指定任务的执行时间,但对任务队列和线程池的管控较弱;一般集成于项目中,小任务很方便。

4、开源工具包 JCronTab

JCronTab 则是一款完全按照 crontab 语法编写的 java 任务调度工具。
特点:

  • 可指定任务的执行时间;
  • 提供完全按照 Unix 的 UNIX-POSIX crontab 的格式来规定时间;
  • 支持多种任务调度的持久化方法,包括普通文件、数据库以及 XML 文件进行持久化;
  • JCronTab 内置了发邮件功能,可以将任务执行结果方便地发送给需要被通知的人;
  • 设计和部署是高性能并可扩展。

5、开源工具包 Quartz

  • 具有强大的调度功能,很容易与 Spring 集成,形成灵活可配置的调度功能;
  • 调度环境的持久化机制:可以保存并恢复调度现场,即使系统因为故障关闭,任务调度现场的数据并不会丢失;timer 没有这些特点;
  • 灵活的应用方式:可以灵活的定义触发器调度的时间表,并可以对触发器与任务进行关联映射;
  • 分布式与集群能力;

二、什么是 Quartz?

Quartz是 OpenSymphony 开源组织在 Job scheduling 领域又一个开源项目,完全由 Java 开发,可以用来执行定时任务,类似于 java.util.Timer。但是相较于 Timer,Quartz 增加了很多功能:

  • 持久性作业 - 就是保持调度定时的状态;
  • 作业管理 - 对调度作业进行有效的管理;

三、Quartz的相关概念

  • Scheduler:调度器,进行任务调度;quartz的大脑。
  • Job:业务job,亦可称业务组件;定时任务的具体执行业务需要实现此接口,调度器会调用此接口的execute方法完成我们的定时业务。
  • JobDetail:用来定义业务Job的实例,我们可以称之为quartz job,很多时候我们谈到的job指的是JobDetail。
  • Trigger:触发器,用来定义一个指定的Job何时被执行。
  • JobBuilder:Job构建器,用来定义或创建JobDetail的实例;JobDetail限定了只能是Job的实例。
  • TriggerBuilder:触发器构建器,用来定义或创建触发器的实例。

四、springboot整合Quartz

1、pom.xml中引入依赖

        <!--quartz依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency>

2、application.xml 中添加配置项

spring:datasource:druid:url: jdbc:mysql://127.0.0.1:3306/my_test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: admin_123# 异步初始化策略,可加快启动速度async-init: true# 初始化时建立物理连接的个数,同最小连接池数量initial-size: 5# 最小连接池数量(按需配置)min-idle: 5# 最大连接池数量(按需配置)max-active: 50# 获取连接超时, -1表示可一直等待max-wait: 6000# 是否缓存preparedStatement,缓存prepared-statements,开启的情况下增加字段可能会报错pool-prepared-statements: false# 缓存preparedStatement cache大小max-open-prepared-statements: 20# 检测连接是否有效的sqlvalidation-query: select 1# 申请连接时执行validationQuery检测连接是否有效test-on-borrow: false# 归还连接时执行validationQuery检测连接是否有效test-on-return: false# 如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。test-while-idle: true# 两个含义:1.Destroy线程运行周期 2.testWhileIdle判断依据time-between-eviction-runs-millis: 60000# 连接保持空闲而不被驱逐的最小时间:5分钟min-evictable-idle-time-millis: 300000# 连接保持空闲而不被驱逐的最大时间: 2天,根据生产mysql配置的wait_time配置=2天max-evictable-idle-time-millis: 172800000# 是否keep-alive:# 即当最小空闲连接空闲了min-evictable-idle-time-millis,执行validationQuery进行keepAlivekeep-alive: true#打印druid统计信息:每天打印一次统计信息日志,后续根据日志帮助优化连接池配置和SQL(按需配置, -1表示关闭)time-between-log-stats-millis: 86400000filter:# 统计filter,druid默认开启stat:enabled: true# 打印慢SQL(如需)log-slow-sql: true# 耗时多久为慢SQL(按需配置)slow-sql-millis: 3000driver-class-name: com.mysql.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcequartz:# 任务存储类型job-store-type: "jdbc"# 关闭时等待任务完成wait-for-jobs-to-complete-on-shutdown: false# 是否覆盖已有的任务overwrite-existing-jobs: true# 是否自动启动计划程序auto-startup: true# 延迟启动startup-delay: 0sjdbc:# 数据库架构初始化模式(never:从不进行初始化;always:每次都清空数据库进行初始化;embedded:只初始化内存数据库(默认值))initialize-schema: "always"# 相关属性配置properties:org:quartz:scheduler:# 调度器实例名称instanceName: QuartzScheduler# 分布式节点ID自动生成instanceId: AUTOjobStore:class: org.springframework.scheduling.quartz.LocalDataSourceJobStoredriverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate# 表前缀tablePrefix: QRTZ_# 是否开启集群isClustered: true# 数据源别名(自定义)dataSource: quartz# 分布式节点有效性检查时间间隔(毫秒)clusterCheckinInterval: 10000useProperties: false# 线程池配置threadPool:class: org.quartz.simpl.SimpleThreadPoolthreadCount: 10threadPriority: 5threadsInheritContextClassLoaderOfInitializingThread: true

3、创建 Quartz 框架使用的 11 张表

方法一: 若在配置项中 initialize-schema: "always" 

项目启动后,在数据库中可以看到自动生成了所有以“qrtz_”开头的表。后面 initialize-schema 改成“never” 就行。

方法二:在压缩包该路径下找到对应的数据库 SQL 执行脚本,拷贝出来去 Navicat 执行;

4、11张表说明

表名说明
qrtz_blob_triggers以Blob 类型存储的触发器
qrtz calendars存放日历信息,quartz可配置一个日历来指定一个时间范围
qrtz_cron triggers存放cron类型的触发器
qrtz fired triggers存放已触发的触发器
qrtz job _details存放一个jobDetail信息
qrtz job listenersjob监听器
qrtz_locks存储程序的悲观锁的信息(假如使用了悲观锁)
qrtz_paused trigger_graps存放暂停掉的触发器
qrtz scheduler state调度器状态
qrtz simple triggers简单触发器的信息
qrtz_trigger_listeners触发器监听器

5、简单的 demo 演示

(1)Service 接口

public interface QuartzService {/*** 新增** @param jobName* @param cron* @param jobClassName* @return*/String addCronJob(String jobName, String cron, String jobClassName);/*** 停止** @param jobName* @param jobGroup* @param triggerName* @param triggerGroup* @return*/String deleteCronJob(String jobName, String jobGroup, String triggerName, String triggerGroup);/*** 立即执行,不定时** @param jobName* @param jobClassName* @return*/String executeImmediately(String jobName, String jobClassName);
}

(2)Service 接口实现类

@Service
@Slf4j
public class QuartzServiceImpl implements QuartzService {@Autowiredprivate Scheduler scheduler;private static final String DEFAULT_JOB_GROUP = "default_job_group";private static final String DEFAULT_TRIGGER_GROUP = "default_trigger_group";private static final String TRIGGER_PRE = "Trigger_";@Overridepublic String addCronJob(String jobName, String cron, String jobClassName) {try {// 当前任务不存在才进行添加JobKey jobKey = JobKey.jobKey(jobName, DEFAULT_JOB_GROUP);if (scheduler.checkExists(jobKey)) {log.info("[添加定时任务]已存在该作业,jobkey为:{}", jobKey);return "已存在该作业";}// 构建 JobJobDetail job = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobKey).build();// cron表达式定时构造器CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);// 构建 TriggerTrigger trigger = TriggerBuilder.newTrigger().withIdentity(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP))
//                .startAt(DateUtil.parseDate(start))
//                .endAt(DateUtil.parseDate(end)).withSchedule(cronScheduleBuilder).build();// 启动调度器scheduler.scheduleJob(job, trigger);scheduler.start();return "SUCCESS";} catch (Exception e) {log.error("[新增定时任务]失败,报错:", e);return "FAIL";}}@Overridepublic String deleteCronJob(String jobName, String jobGroup, String triggerName, String triggerGroup) {try {JobKey jobKey = JobKey.jobKey(jobName, jobGroup);TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);Trigger trigger = scheduler.getTrigger(triggerKey);if (null == trigger) {log.info("[停止定时任务]根据triggerName:{}和triggerGroup:{}未查询到相应的trigger!");return "SUCCESS";}//暂停触发器scheduler.pauseTrigger(triggerKey);// 移除触发器scheduler.unscheduleJob(triggerKey);// 删除任务scheduler.deleteJob(jobKey);log.info("[停止定时任务]jobName:{},jobGroup:{}, triggerName:{}, triggerGroup:{},停止--------------", jobName, jobGroup, triggerName, triggerGroup);return "SUCCESS";} catch (SchedulerException e) {log.error("[停止定时任务]失败,报错:", e);return "FAIL";}}public static Job getClass(String className) throws Exception {Class<?> classTemp = Class.forName(className);return (Job) classTemp.newInstance();}@Overridepublic String executeImmediately(String jobName, String jobClassName) {try {JobKey jobKey = JobKey.jobKey(jobName, DEFAULT_JOB_GROUP);JobDetail job = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobKey).build();Trigger trigger = TriggerBuilder.newTrigger().withIdentity(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP)).build();// 启动调度器scheduler.scheduleJob(job, trigger);scheduler.start();return "SUCCESS";} catch (Exception e) {log.error("[立即执行一次任务,不定时]失败,报错:", e);return "FAIL";}}
}

(3)具体的业务类

@Component
@Slf4j
public class TaskJob implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {log.info("=========================业务逻辑====================");log.info("jobName:{}", jobExecutionContext.getJobDetail().getKey().getName());log.info("jobGroup:{}", jobExecutionContext.getJobDetail().getKey().getGroup());log.info("triggerName:{}", jobExecutionContext.getTrigger().getKey().getName());log.info("triggerGroup:{}", jobExecutionContext.getTrigger().getKey().getGroup());log.info("上次触发时间:{}", DateUtil.formatDateTime(jobExecutionContext.getPreviousFireTime()));log.info("本次触发时间:{}", DateUtil.formatDateTime(jobExecutionContext.getFireTime()));log.info("下次触发时间:{}", DateUtil.formatDateTime(jobExecutionContext.getNextFireTime()));log.info("调度时间:{}", DateUtil.formatDateTime(jobExecutionContext.getScheduledFireTime()));}
}

(4)入参对象

@Data
public class JobInfo {private String jobName;private String cron;private String jobGroup;private String triggerName;private String triggerGroup;}

(5)暴露接口层

@RestController
@RequestMapping("/quartz")
public class QuartzController {@Autowiredprivate QuartzService quartzService;@PostMapping("/createJob")public String createJob(@RequestBody JobInfo jobInfo) {return quartzService.addCronJob(jobInfo.getJobName(), jobInfo.getCron(), "com.example.springbootzy.quartz.config.TaskJob");}@PostMapping("/deleteJob")public String deleteJob(@RequestBody JobInfo jobInfo) {return quartzService.deleteCronJob(jobInfo.getJobName(), jobInfo.getJobGroup(), jobInfo.getTriggerName(), jobInfo.getTriggerGroup());}@PostMapping("/executeImmediately")public String executeImmediately(@RequestBody JobInfo jobInfo) {return quartzService.executeImmediately(jobInfo.getJobName(), "com.example.springbootzy.quartz.config.TaskJob");}}

(6)测试:新增一个“每十秒钟执行一次”的定时任务

(7)测试:删除上述已创建的定时任务

(8)测试:只执行一次

(9)其他

// 任务暂停
scheduler.pauseTrigger(TriggerKey.triggerKey("Trigger的name","Trigger的group"));// 任务恢复
scheduler.resumeTrigger(TriggerKey.triggerKey("Trigger的name","Trigger的group"));

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

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

相关文章

STM32存储左右互搏 QSPI总线读写FLASH W25QXX

STM32存储左右互搏 QSPI总线读写FLASH W25QXX FLASH是常用的一种非易失存储单元&#xff0c;W25QXX系列Flash有不同容量的型号&#xff0c;如W25Q64的容量为64Mbit&#xff0c;也就是8MByte。这里介绍STM32CUBEIDE开发平台HAL库Qual SPI总线操作W25Q各型号FLASH的例程。 W25Q…

【通讯录案例-沙盒路径 Objective-C语言】

一、接下来,我们来学习“存储”的相关的一些东西, 1.打开这个“数据存储“的ppt, 打开这个ppt, 首先呢,关于存储这一块儿, 存储呢,首先,有常见的几种方式,注意啊,这个里边儿是“方式”,方式,什么意思,是表示怎么去存,有五种方式: 1)XML属性列表(plist)归档:…

【数据结构】(三)树Tree

目录 1、基本概念 2、二叉树Binary Tree 3、树、森林与二叉树的转换 4、赫夫曼树Huffman Tree与赫夫曼编码Huffman Coding 1、基本概念 &#xff08;1&#xff09;树&#xff08;Tree&#xff09;是 n&#xff08;n ≥\geq 1&#xff09;个节点的有限集&#xff0c;n 0时称…

记elasticsearch CPU负载100%问题

记elasticsearch CPU负载100%问题 环境&#xff1a;问题表现&#xff1a;初步排查&#xff1a;日志查询hot_thread 深入查询当前elasticsearch正在运行的Task查看Task详情解决问题对导致问题的原因的几个猜测问题复现&#xff1a;导致问题的原因。json导入规则问题json导入规则…

Linux系统安全之iptables防火墙

目录 一、iptables防火墙的基本介绍 1、netfile与iptables的关系 1.1netfile 1.2iptables 1.3iptables是基于内核的防火墙&#xff0c;其中内置了raw&#xff0c;mangle&#xff0c;nat和filter四个规则表 2、iptables防火墙默认规则表&#xff0c;链结构 二、iptables的…

HashSet 的基本操作【集合容器知识回顾 ④】

HashSet 是 Java 中的一个集合类&#xff0c;它实现了 Set 接口。Set 是一种不允许包含重复元素的集合&#xff0c;而 HashSet 则是 Set 接口的一个具体实现。因此&#xff0c;HashSet 用于存储一组唯一的元素&#xff0c;不允许重复。 HashSet 的一些特点&#xff1a; 不允许…

漏洞03-CSRF漏洞

CSRF漏洞 文章目录 CSRF CSRF

链式二叉树(1)

目录 二叉树的概念&结构 二叉树的遍历概念 手写二叉树测试 二叉树遍历实现代码 递归图解 前序遍历递归图解 中序序遍历递归图解 后续遍历递归图解 二叉树的概念&结构 二叉树是&#xff1a; 空树非空&#xff1a;根节点&#xff0c;根节点的左子树、根节点的右子…

3ASC25H216A DATX132

3ASC25H216A DATX132 3ASC25H216A DATX132 3ASC25H216A DATX132在DAQ应用中使用非隔离DC/DC电源降压模块的优势 "... 图3&#xff1a;德州仪器电源模块电感HTS性能 此外&#xff0c;我们的电源 ... 响应。 图4&#xff1a;功率模块效率和负载瞬态响应 若保持 ...…

Java设计模式-组合模式(13)

大家好,我是馆长!今天开始我们讲的是结构型模式中的组合模式。老规矩,讲解之前再次熟悉下结构型模式包含:代理模式、适配器模式、桥接模式、装饰器模式、外观模式、享元模式、组合模式,共7种设计模式。 组合模式(Composite Pattern) 定义 组合(Composite)模式:又叫…

【开源】基于Qt5的ROS1/ROS2人机交互软件(支持地图编辑/多点导航)

本项目基于Qt5开发&#xff0c;基于CMake进行构建&#xff0c;可以实现一套代码同时在ROS1/ROS2系统中使用(本项目已接入CI,保证多ROS版本/系统版本可用性) 项目地址&#xff1a; https://github.com/chengyangkj/Ros_Qt5_Gui_App 软件在编译时会自动识别环境变量中的ROS1/ROS…

如何对视频进行翻译

下载视频和翻译软件 视频和翻译软件点击下载就行了&#xff0c;下载之后解压&#xff0c;然后把两个exe点一下。接下来如果资金充裕或者要求比较高的可以使用各个api&#xff0c;网站里有视频介绍了。 经济适用视频翻译 原理简析 首先这个软件对视频的翻译的流程大致如下&a…

【GameFramework框架】一、框架介绍

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录&#xff1a; https://blog…

tableau绘制雷达图

目标图形: 1. 数据准备 &#xff08;1&#xff09;原始数据 你要进行用雷达图比较的对象的各指标的数据。 (2) 处理后数据 在原数据的基础上添加对各指标进行区间的划分数据&#xff0c;也就是层级的划分。 2. 操作步骤 &#xff08;1&#xff09;数据转化 转化前&#xf…

Backtrader 文档学习- Broker - Cheat-On-Open

Backtrader 文档学习- Broker - Cheat-On-Open 1.概述 V1.9.44.116增加了Cheat On Open的支持。对于全押的人来说&#xff0c;这似乎是一个必需的功能&#xff0c;用bar的收盘价后进行计算&#xff0c;希望与开盘价相匹配。 当开盘价差距&#xff08;上涨或下跌&#xff0c;取…

streampark+flink一键整库或多表同步mysql到doris实战

streamparkflink一键整库或多表同步mysql到doris实战&#xff0c;此应用一旦推广起来&#xff0c;那么数据实时异构时&#xff0c;不仅可以减少对数据库的查询压力&#xff0c;还可以减少数据同步时的至少50%的成本&#xff0c;还可以减少30%的存储成本&#xff1b; streampar…

Vue.js 学习14 集成H265web.js播放器实现webpack自动化构建

Vue.js 学习14 集成H265web.js播放器实现webpack自动化构建 一、项目说明1. H265web.js 简介2. 准备环境 二、项目配置1. 下载 H265web.js2. 在vue项目里引入 H265web3. 设置 vue.config.js 三、代码引用1. 参照官方demo &#xff0c; 创建 executor.js2. 在 vue 页面里引用htm…

K8S网络

一、介绍 k8s不提供网络通信&#xff0c;提供了CNI接口(Container Network Interface&#xff0c;容器网络接口)&#xff0c;由CNI插件实现完成。 1.1 Pod通信 1.1.1 同一节点Pod通信 Pod通过虚拟Ethernet接口对&#xff08;Veth Pair&#xff09;与外部通信&#xff0c;Veth…

有向图查询所有环,非递归

图&#xff1a; 有向图查询所有环&#xff0c;非递归&#xff1a; import java.util.*;public class CycleTest {private final int V; // 顶点数private final List<List<Integer>> adjList; // 邻接表public CycleTest(int vertices) {this.V vertices;this.…

C++ : 类的简单介绍(四)——析构函数

概念&#xff1a; 与构造函数功能相反&#xff0c;析构函数不是完成对对象本身的销毁&#xff0c;局部对象销毁工作是由 编译器完成的。 而对象在销毁时会自动调用析构函数&#xff0c;完成对象中资源的清理工作。 特征&#xff1a; 1. 析构函数名是在类名前加上字符 ~ 2. …