【开发篇】十四、SpringBoot整合Quartz实现定时任务

news2024/12/25 21:46:04

文章目录

  • 1、关于定时任务
  • 2、Java原生实现
  • 3、相关名词
  • 4、SpringBoot整合Quartz
  • 5、Quartz的通用配置
  • 6、关于QuartzJobBean
  • 7、关于调度器Scheduler的绑定
  • 8、Quartz持久化

1、关于定时任务

定时任务在实际开发中使用场景很多,比如:

  • 年度报告
  • 各种统计报告
  • 某个同步任务

而定时任务的实现技术,业界流行的有:

  • Quartz
  • Spring Task

2、Java原生实现

demo代码如下:TimerTask是一个抽象类,new它的对象要实现方法,而它里面的run方法,应该能想到的是多线程也有个run,而定时任务也确实是这个run,到时间了以后就去开一个异步线程来执行任务。(TimerTask实现了Runnable接口)

import java.util.Timer;
import java.util.TimerTask;

public class TaskTest{

	public static void main(String[] args){
	
		Timer timer = new Timer();
		
		TimerTask task = new TimerTask(){
			@Override
			public void run(){
				System.out.println("task...");
			}
		}
		
		timer.schedule(task,0,2000);  //传入要执行的任务,0即从现在开始,2000即每隔两秒执行一次
	}
}

简单执行下,效果为每两秒输出以下task…

在这里插入图片描述

以上为原生Java的实现,在此基础上,市面上出现了更加完善和规范的落地技术Quartz,于是,Spring整合了Quartz,后来,Spring又推出了自己的Spring Task

3、相关名词

  • 工作(Job):用于定义具体执行的工作
  • 工作明细(JobDetail):用于描述定时工作相关的信息
  • 触发器(Trigger):用于描述触发工作的规则,通常使用cron表达式定义调度规则
  • 调度器(Scheduler):描述了工作明细与触发器的对应关系

其中,Job被JobDetail绑定,JobDetail又被Trigger绑定。

4、SpringBoot整合Quartz

导入Quartz起步依赖坐标:

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

继承QuartzJobBean,定义具体要执行的任务:

public class QuartzTaskBean extends QuartzJobBean {
	@Override    
	protected void executeInternal(JobExecutionContext context) throws JobExecutionException {        

		//...这里写定时任务的逻辑代码
		System.out.println("quartz job run... ");    
	}
}

定义工作明细与触发器,并绑定对应关系:

@Configuration
public class QuartzConfig {    

	@Bean    
	public JobDetail printJobDetail(){        
		return JobBuilder.newJob(QuartzTaskBean.class) //传入上面的Job类,实现绑定到JobDetail
				.storeDurably()  //持久化,storeDurably()方法是用来持久化的,即Job创建完以后若当时没有使用,是否要持久化一下先存起来
				.build();
    }    
    
    @Bean    
    public Trigger printJobTrigger() {        
	    CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/3 * * * * ?");       
	    return TriggerBuilder.newTrigger()
    			.forJob(printJobDetail())  //绑定JobDetail
            	.withSchedule(cronScheduleBuilder)
            	.build();    
	}
}

整合完成,重启服务看下效果:

在这里插入图片描述

5、Quartz的通用配置

以上整合中,不用加任何配置,采用默认配置就完成了定时任务。但还可以在服务的yml配置文件中对Quartz进行更详细的配置。(可选)

spring:
  # Quartz 的配置,对应 QuartzProperties 配置类
  quartz:
    job-store-type: memory # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。
    auto-startup: true # Quartz 是否自动启动
    startup-delay: 0 # 延迟 N 秒启动
    wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
    overwrite-existing-jobs: false # 是否覆盖已有 Job 的配置
    properties: # 添加 Quartz Scheduler 附加属性
      org:
        quartz:
          threadPool:
            threadCount: 25 # 线程池大小。默认为 10 。
            threadPriority: 5 # 线程优先级
            class: org.quartz.simpl.SimpleThreadPool # 线程池类型
#    jdbc: # 这里暂时不说明,使用 JDBC 的 JobStore 的时候,才需要配置

这些yaml配置自然有对应的实体类去读取和接收,这个实体类就是QuartzProperties搭配@ConfigurationProperties注解:

在这里插入图片描述

6、关于QuartzJobBean

QuartzJobBean类的源码:它实现了Job,并且定义了公用的execute方法

public abstract class QuartzJobBean implements Job {

	/**
	 * This implementation applies the passed-in job data map as bean property
	 * values, and delegates to {@code executeInternal} afterwards.
	 * @see #executeInternal
	 */
	@Override
	public final void execute(JobExecutionContext context) throws JobExecutionException {
		try {
             // 将当前对象包装为BeanWrapper
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            // 设置属性
			MutablePropertyValues pvs = new MutablePropertyValues();
			pvs.addPropertyValues(context.getScheduler().getContext());
			pvs.addPropertyValues(context.getMergedJobDataMap());
			bw.setPropertyValues(pvs, true);
		}
		catch (SchedulerException ex) {
			throw new JobExecutionException(ex);
		}
        // 子类实现该方法
		executeInternal(context);
	}

	/**
	 * Execute the actual job. The job data map will already have been
	 * applied as bean property values by execute. The contract is
	 * exactly the same as for the standard Quartz execute method.
	 * @see #execute
	 */
	protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException;

}

因此,创建Job的时候,可以继承QuartzJobBean类,也可直接实现Job接口,但继承QuartzJobBean并实现executeInternal方法,当然效率最高

7、关于调度器Scheduler的绑定

Scheduler绑定有两种方式,一种是使用bena的自动配置,一种是Scheduler手动配置。上面整合时,采用的是自动配置的方式,即定义两个Bean以及它们之间的关系,然后交给Spring容器管理。这里再补充一下手动配置的方式:实现Spring的ApplicationRuunner接口,注入Quartz的Scheduler对象,调用scheduleJob方法完成手动绑定

@Component
public class QuartzJob implements ApplicationRunner {

    @Resource
    private Scheduler scheduler;

    @Override
    public void run(ApplicationArguments args) throws Exception {
    	//创建JobDetail对象
        JobDetail jobDetail = JobBuilder.newJob(QuartzTaskBean.class)
                .storeDurably()
                .build();
        // 简单的调度计划的构造器
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(5) // 频率
                .repeatForever(); // 次数
                
        // 创建任务触发器
        Trigger trigger = TriggerBuilder.newTrigger()
                .forJob(jobDetail)
                .withSchedule(scheduleBuilder)
                .startNow() //立即执行一次任務
                .build();
                
        // 手动将触发器与任务绑定到调度器内
        scheduler.scheduleJob(jobDetail, trigger);
    }
}


注意,这个类要注册成一个Bean,记得放在被Spring容器能扫描到的目录下。此外,上面没有用Cron表达式的调度器,用的简单的调度计划,调这个类提供的方法来描述你的执行规则就行。

在这里插入图片描述

8、Quartz持久化

Quartz持久化配置提供了两种存储器:RAMJobStore和JDBCJobStore,就是内存和硬盘:默认是内存形式维护任务信息,意味着服务重启了任务就从头再来,之前的执行数据等就全没了

存储方式优点缺点
RAMJobStore不要外部数据库,配置容易,运行速度快因为调度程序信息是存储在被分配给 JVM 的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。另外因为存储到JVM内存里面,所以可以存储多少个 Job 和 Trigger 将会受到限制
JDBCJobStore支持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务运行速度的快慢取决与连接数据库的快慢

下面实现持久化到MySQL,首先创建Quartz数据库,脚本在:

org\quartz-scheduler\quartz\2.3.2\quartz-2.3.2.jar!\org\quartz\impl\jdbcjobstore\tables_mysql_innodb.sql

在这里插入图片描述

这里有各种数据库类型的SQL脚本,执行完MySQL后查看表:

mysql> use quartz;
Database changed
mysql> show tables;
+--------------------------+
| Tables_in_quartz         |
+--------------------------+
| qrtz_blob_triggers       |## blog类型存储triggers
| qrtz_calendars           |## 以blog类型存储Calendar信息
| qrtz_cron_triggers       |## 存储cron trigger信息
| qrtz_fired_triggers      |## 存储已触发的trigger相关信息
| qrtz_job_details         |## 存储每一个已配置的job details
| qrtz_locks               |## 存储悲观锁的信息
| qrtz_paused_trigger_grps |## 存储已暂停的trigger组信息
| qrtz_scheduler_state     |## 存储Scheduler状态信息
| qrtz_simple_triggers     |## 存储simple trigger信息
| qrtz_simprop_triggers    |## 存储其他几种trigger信息
| qrtz_triggers            |## 存储已配置的trigger信息
+--------------------------+


加MySQL驱动以及JDBC的依赖坐标(看技术选型,采用MySQL驱动+MyBatis的不用加,MyBatis起步依赖下面包含Jdbc Starter)

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!--看当前项目的已有依赖,很多封装JDBC的框架starter包含jdbc-starter-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!--数据源自己选吧,我用的druid,一般接手的项目这些肯定都有了-->

修改application.yml:

# 旧的数据源配置,用的druid
spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/test_quartz?serverTimezone=GMT%2B8
      username: root
      password: root123

  # quartz持久化的关键配置
  quartz:
    job-store-type: jdbc # 使用数据库存储
    scheduler-name: testScheduler # 相同 Scheduler 名字的节点,形成一个 Quartz 集群
    wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
    jdbc:
      initialize-schema: never # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。可以选择always,never,embedded,第一次也可偷懒让框架去创建表

重启服务,可以看到持久化成功:

在这里插入图片描述

除了以上关键配置外,集群、线程池等Quartz Scheduler 附加属性相关配置可参考以下这个COPY的比较全的配置文件:

spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/test_quartz?serverTimezone=GMT%2B8
      username: root
      password: root123
  quartz:
    job-store-type: jdbc # 使用数据库存储
    scheduler-name: hyhScheduler # 相同 Scheduler 名字的节点,形成一个 Quartz 集群
    wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
    jdbc:
      initialize-schema: never # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。可以选择always,never,embedded,第一次也可偷懒让框架去创建表
    properties:
      org:
        quartz:
          # JobStore 相关配置
          jobStore:
            dataSource: quartzDataSource # 使用的数据源
            class: org.quartz.impl.jdbcjobstore.JobStoreTX # JobStore 实现类
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_ # Quartz 表前缀
            isClustered: true # 是集群模式
            clusterCheckinInterval: 1000
            useProperties: false
          # 线程池相关配置
          threadPool:
            threadCount: 25 # 线程池大小。默认为 10 。
            threadPriority: 5 # 线程优先级
            class: org.quartz.simpl.SimpleThreadPool # 线程池类型


配置中有一个scheduler-name字段,看上面Quartz的库里的表,所有的表中都含有一个SCHED_NAME字段,对应配置的这个scheduler-name,相同 Scheduler-name的节点,形成一个 Quartz 集群。

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

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

相关文章

vs code 离线安装 CodeLLDB 包[Acquiring CodeLLDB platform package]

1. 问题描述 最近在配置使用vscode编译c&#xff0c;一打开vscode就弹出以下信息“Acquiring CodeLLDB platform package” 2. 问题原因 vscode在安装CodeLLDB插件时&#xff0c;速度太慢&#xff0c;一直不能成功 3. 解决方案&#xff1a; 离线下载 CodeLLDB插件&#xff0c…

前后端通信到底是怎样一个过程

前后端通信是怎样 前言&#xff1a;Http协议 超文本传输协议 规定&#xff1a;每一次前后端通信&#xff0c;前端需要主动向后端发出请求&#xff0c;后端接收到前端的请求后&#xff0c;可以给出响应 1、Http报文 浏览器向服务器发送请求时&#xff0c;请求本身就是信息&…

ROS导航——环境感知(激光雷达)

下载相关驱动包&#xff08;激光雷达厂商应该会给出&#xff09; 编译后可能会出现部分错误&#xff0c;以下是部分情况&#xff1a; &#xff08;1&#xff09; 移植功能包后出现c文件无法找到头文件的情况&#xff1a;解决链接 修改代码&#xff1a;&#xff08;以我的雷达为…

将pyc文件转换为py文件

1.首先将pip版本升级 pip install --upgrade pip 2.然后安装uncompyle6 pip install uncompyle6 3.在系统的环境变量中&#xff0c;添加“python_home” 4.在系统变量Path中添加&#xff1a; %python_home%\Scripts\ 5.运行下面的代码&#xff0c;就会在你.pyc对应文件夹…

腾讯云服务器完整建站过程(新手搭建网站教程)

使用腾讯云服务器搭建网站全流程&#xff0c;包括轻量应用服务器和云服务器CVM建站教程&#xff0c;轻量可以使用应用镜像一键建站&#xff0c;云服务器CVM可以通过安装宝塔面板的方式来搭建网站&#xff0c;腾讯云服务器网分享使用腾讯云服务器建站教程&#xff0c;新手站长搭…

第二章 进程与线程 十九、管程

目录 一、定义 管程是一种特殊的软件模块&#xff0c;由以下部分组成&#xff1a; 二、管程的基本特征 三、使用管程解决生产者消费者问题 四、总结 一、定义 管程是一种特殊的软件模块&#xff0c;由以下部分组成&#xff1a; 1、局部于管程的共享数据结构说明;&#xf…

[QT编程系列-45]: 内存检测工具Dr.Memory在Windows上的使用实践与详解

目录 一、使用前的澄清 二、下载地址 三、功能概述 四、 使用方法与步骤 4.1 常见命令 4.2 命令选项详解 4.3 常见问题监测 4.3.1 内存泄露相关参数 4.4 结果输出参数 4.5 输出分析 一、使用前的澄清 &#xff08;1&#xff09;之前在https://blog.csdn.net/fengbin…

SNAP与Sen2Cor下载与安装

SNAP软件下载与安装 一、下载地址 首先进入网站 找到DOWNLOAD下载页&#xff0c; 安装完成后&#xff0c;界面如下 还需要再装一个Sen2cor下载好之后&#xff0c;解压到用户文件夹下 然后打开L2A_Process.bat文件 打开CMD&#xff0c;输入 cd C:\Users\lenovo\AppData\L…

【算法训练-二分查找 一】【基本二分】二分查找、在排序数组中查找元素的第一个和最后一个位置

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是螺旋矩阵&#xff0c;使用【二维数组】这个基本的数据结构来实现 二分查找【EASY】 从最简单的二分查找入手&#xff0c;进而开始解决一系列其变体…

【Linux基础】Linux云服务器(腾讯云、阿里云、华为云)环境部署 | 安装远程XShell | 基本账号管理(超详细教程)

&#x1f449; 系列专栏&#xff1a;【LLinux基础】 &#x1f648; 个人主页&#xff1a;sunnyll 目录 一、前言 二、 Linux环境安装 &#x1f4a6; Linux 环境的搭建方式 &#x1f4a6;如何购买云服务器 三、 安装远程控制XShell &#x1f4a6;下载 XShell &#x1f4…

阿里云对象存储OSS SDK的使用

官方文档 https://help.aliyun.com/zh/oss/developer-reference/java 准备工作 windows安装好JDK&#xff0c;这里使用JDK1.8为例 windows安装好IDEA&#xff0c;这里使用IDEA2022 登录阿里云控制台&#xff0c;通过免费试用OSS或开通OSS 步骤 配置访问凭证 有临时和长期…

STM32F4学习笔记读取芯片UID和MAC地址

一、简介 在嵌入式设备开发过程中有时会需要为设备设置唯一的ID用以标识设备唯一&#xff0c;比如要求同一总线上的所有设备ID不能重复&#xff0c;要求设备具体唯一的MAC地址等等。每个STM32微控制器都自带一个96位的唯一ID&#xff0c;这个ID在任何情况下都是唯一且不允许修…

谷歌地球引擎GEE账户注册的快速、百分百成功方法

本文介绍免费注册谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;账户的方便、快捷的最新方法&#xff1b;基于这一方法&#xff0c;只要我们创建一个谷歌Cloud Project&#xff0c;就可以直接访问GEE。 GEE在原本&#xff08;大概前几年的时候&#…

day 10.4

服务器 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QLine> #include <QTcpServer> #include <QTcpSocket> #include <QMessageBox>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Wid…

Java中如何实现定时任务?

文章目录 定时任务基本介绍前言基本概念介绍线程类实现定时任务Thread类实现定时任务Runnable接口实现定时任务Callable接口实现定时任务 Timer实现定时任务Timer的常用方法Timer的优缺点schedule和scheduleAtFixedRate的区别代码示例 ScheduledExecutorService实现定时Schedul…

僵尸进程的产生与处理

僵尸进程&#xff08;Zombie Process&#xff09;是指在操作系统中已经完成了执行&#xff0c;但其父进程尚未调用wait()或waitpid()来获取其终止状态的子进程。当一个进程结束时&#xff0c;操作系统会保留该进程的一些基本信息&#xff0c;包括进程ID&#xff08;PID&#xf…

C++_pen_类

类的成员函数 构造函数析构函数普通成员函数 构造函数与析构函数 #include <stdio.h> class STU{ public:STU(){printf("STU\n");}STU(int id){printf("STU(int id)\n");}~STU(){printf("STU Bye!!!\n");} };int main(int argc, char c…

图像分类概述

图像分类概述 图像分类&#xff0c;也称图像识别&#xff0c;是计算机根据已有的固定分类标签集合和图像所反馈的信息特征从标签集合中找出一个分类标签&#xff0c;并分配给该图像的视觉处理方法。 譬如规定一个分类标签为猫和狗的集合&#xff0c;给计算机输入一张猫或狗的…

【产品经理】国内企业服务SAAS平台的生存与发展

SaaS在国外发展的比较成熟&#xff0c;甚至已经成为了主流&#xff0c;但在国内这几年才掀起热潮&#xff1b;企业服务SaaS平台在少部分行业发展较快&#xff0c;大部分行业在国内还处于起步、探索阶段&#xff1b;SaaS将如何再国内生存和发展&#xff1f; 在企业服务行业做了五…