前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot整合Quartz定时任务(持久化到数据库)

SpringBoot整合Quartz定时任务(持久化到数据库)

作者头像
全栈程序员站长
发布2022-07-25 11:01:12
1.8K0
发布2022-07-25 11:01:12
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

背景

最近在做项目,项目中有个需求:需要使用定时任务,这个定时任务需要即时生效。 查看Quartz官网之后发现:Quartz提供两种基本作业存储类型:

  1. RAMJobStore :RAM也就是内存,默认情况下Quartz会将任务调度存在内存中,这种方式性能是最好的,因为内存的速度是最快的。不好的地方就是数据缺乏持久性,但程序崩溃或者重新发布的时候,所有运行信息都会丢失
  2. JDBC作业存储:存到数据库之后,可以做单点也可以做集群,当任务多了之后,可以统一进行管理。关闭或者重启服务器,运行的信息都不会丢失。缺点就是运行速度快慢取决于连接数据库的快慢。

所以决定采用 JDBC作业存储的方式。

为什么需要持久化?

  1. 以后可以做集群。
  2. 任务可以进行管理,随时停止、暂停、修改任务。

你应该了解的概念

因为之前接触过quartz这个任务调度框架,所以对quartz有一定的了解,quartz三要素:Scheduler、Trigger、JobDetai&Job。 突然想起来,之前写过介绍-。- 自己都忘了,贴上地址,需要的童鞋可以先去了解下: https://blog.csdn.net/bicheng4769/article/details/81097305

SpringBoot集成Quartz

我们也可以自己去将quartz和springBoot整合在一起,其实说是springBoot还不如说是sping,因为我们没有用到spirngboot的相关的快捷方式。 如果童鞋们想快速集成Quartz,立刻看到效果的话,可以直接往下翻,直接看SpirngBoot自带的Quartz插件。但我建议大家还是从spring整合Quartz开始,懂的原理,方有收获。

Quartz初始化表

如果需要做持久化的话,数据肯定是要存在数据库的,那么到底存在哪些表呢?其实官网文档也跟我们讲过了,地址如下: http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-09.html 其中有句话:

JDBCJobStore works with nearly any database, it has been used widely with Oracle, PostgreSQL, MySQL, MS SQLServer, HSQLDB, and DB2. To use JDBCJobStore, you must first create a set of database tables for Quartz to use. You can find table-creation SQL scripts in the “docs/dbTables” directory of the Quartz distribution.

荣老夫这个四级的水平给你们翻译下: 大概就是支持这么多的数据库类型。如果你要使用JDBCJoBStore的话,你先要创建一些表,这些表在 “doc/dbTables”里面。“doc/dbTables” 在哪儿呢?其实都在源码里面,直接到官网下下来就行了。

Spring整合Quartz

  1. pom文件加入相关jar
  2. 相关配置文件(不管是properties 还是yml。采用JDBC存储)
  3. 业务逻辑层中使用。

pom文件

如下所示:

代码语言:javascript
复制
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--quartz -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
            <!-- <version>2.3.0</version> -->
        </dependency>
        <!--定时任务需要依赖context模块-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <!--&lt;!&ndash; druid数据库连接池 &ndash;&gt;-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
    </dependencies>

对应的properties 文件

代码语言:javascript
复制
#使用自己的配置文件
org.quartz.jobStore.useProperties:true

#默认或是自己改名字都行
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
#如果使用集群,instanceId必须唯一,设置成AUTO
org.quartz.scheduler.instanceId = AUTO


org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true


#存储方式使用JobStoreTX,也就是数据库
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#是否使用集群(如果项目只部署到 一台服务器,就不用了)
org.quartz.jobStore.isClustered = false
org.quartz.jobStore.clusterCheckinInterval=20000
org.quartz.jobStore.tablePrefix = qrtz_
org.quartz.jobStore.dataSource = myDS

#配置数据源
#数据库中quartz表的表名前缀
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/aipyun?serverTimezone=GMT&characterEncoding=utf-8
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = root123
org.quartz.dataSource.myDS.maxConnections = 5

核心QuartzConfiguration类:

代码语言:javascript
复制
ackage com.cj.config;

import org.quartz.Scheduler;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * 描述 : quartz 配置信息
 *
 * @author caojing
 * @create 2018-12-24-16:47
 */
@Configuration
public class QuartzConfiguration {
    @Autowired
    private JobFactory jobFactory;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(jobFactory);
        // 用于quartz集群,QuartzScheduler 启动时更新己存在的Job
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        //延长启动
        schedulerFactoryBean.setStartupDelay(1);
        //设置加载的配置文件
        schedulerFactoryBean.setConfigLocation(new ClassPathResource("/quartz.properties"));
        return schedulerFactoryBean;
    }

    @Bean
    public Scheduler scheduler() {
        return schedulerFactoryBean().getScheduler();
    }
}

这其中我们把2个类的初始化移到了IOC中,因为之前Quartz的实例化是自己去控制的,为什么要这么做后面会有讲到。 一个是SchedulerFactoryBean类,这个类其实就是之前xml配置中的SchedulerFactoryBean。附上之前xml配置如下(这里不需要配置,springboot建议我们少用xml配置)

代码语言:javascript
复制
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="oceanStatusCronTrigger"/>
        </list>
    </property>
</bean>

这个类我相信只要用过xml配置的人一定很熟悉,这是Quartz入口。同时也是spring 和Scheduler 关系的桥梁。以便在Spring容器启动后,Scheduler自动开始工作,而在Spring容器关闭前,自动关闭Scheduler。

JobFactory类

代码语言:javascript
复制
package com.cj.config;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.stereotype.Component;

/**
 * 描述:
 *
 * @author caojing
 * @create 2018-12-26-14:03
 */
@Component
public  class JobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory  capableBeanFactory;


    @Override
    protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
        // 调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        // 进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

这个类的作用就是讲Job的实例化交给IOC去进行。 其实问题在于: Job对象的实例化过程是在Quartz中进行的,注入的实体类是在Spring容器当中的 所以在job中无法注入Srping容器的实体类。 解决方案:将Job Bean也纳入到Spring容器的管理之中,Spring容器自然能够为Job Bean自动装配好所需的依赖。 如何纳入:Job的创建都是通过JobFactory创建的。官网解释为证: https://www.quartz-scheduler.org/api/2.2.1/org/quartz/spi/JobFactory.html

A JobFactory is responsible for producing instances of Job classes.

翻译:JobFactory负责生成Job类的实例。 JobFactory 有2个实现类:AdaptableJobFactory 和 SimpleJobFactory。

  1. 自定义的工厂类 JobFactory 继承 AdaptableJobFactory 。
  2. 通过调用父类 AdaptableJobFactory 的方法createJobInstance来实现对Job的实例化。
  3. 在Job实例化完以后,再调用自身方法为创建好的Job实例进行属性自动装配并将其纳入到Spring容器的管理之中。(通过AutowireCapableBeanFactory纳入)。截胡~~~~

UploadTask 类:

代码语言:javascript
复制
package com.cj.quartzdemo;
import com.cj.controller.IndexController;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * 描述:
 *
 * @author caojing
 * @create 2018-12-25-11:38
 */
@Component
@DisallowConcurrentExecution
public class UploadTask extends QuartzJobBean {
    @Autowired
    private IndexController indexController;
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println(new Date() + "任务开始------------------------------------");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(new Date() + "任务结束------------------------------------");
    }
}

继承QuartzJobBean类,重写executeInternal方法。 附:DisallowConcurrentExecution 比如job执行10秒,任务是每隔5秒执行,加上这个注解,程序就会等10秒结束后再执行下一个任务。

indexController类:

代码语言:javascript
复制
package com.cj.controller;

import com.cj.quartzdemo.UploadTask;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * 描述:
 *
 * @author caojing
 * @create 2018-12-26-14:11
 */
@Controller
public class IndexController {
    @Autowired
    private Scheduler scheduler;

    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public void index() throws SchedulerException {
  	//cron表达式
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/8 * * * * ?");
        //根据name 和group获取当前trgger 的身份
        TriggerKey triggerKey = TriggerKey.triggerKey("cj", "123");
        CronTrigger triggerOld = null;
        try {
        	//获取 触发器的信息
            triggerOld = (CronTrigger) scheduler.getTrigger(triggerKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        if (triggerOld == null) {
        	//将job加入到jobDetail中
            JobDetail jobDetail = JobBuilder.newJob(UploadTask.class).withIdentity("cj", "123").build();
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity("cj","123").withSchedule(cronScheduleBuilder).build();
            //执行任务
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            System.out.println("当前job已存在--------------------------------------------");
        }
    }
}

浏览器输入 http://localhost:8080/index 就可以看到数据库已经存储了我们写的cron表达式和相应的类。 查看数据库表(qrtz_cron_triggers)附上截图:

任务信息
任务信息

至此,job 已经被我们成功持久化到数据库。我们来回顾下整体的一个流程。

  1. pom文件添加对应的依赖。
  2. mysql数据库对应表的初始化。
  3. 配置对应的properties
  4. 将原来quartz控制的类的实例化交给spirng IOC控制。(对应的是核心QuartzConfiguration类和JobFactory类)
  5. 业务逻辑层对job进行控制。

总结

其实思路整理一下,我们发现过程其实还是挺简单的,唯一可能有些困难的是对QuartzConfiguration类和JobFactory类的理解。这两个类也是整合的核心类。 但是在springboot2.0之后,我发现了一个很神奇的starter。

代码语言:javascript
复制
spring-boot-starter-quartz

用了这个,那两个核心的类就不需要写了,因为 spring-boot-starter-quartz 已经帮我们整理完成,下一章给大家介绍一种更为简单的整合方式。 具体的对job的增删改查的代码大家可以看下一篇博客~ 附上源码下载地址: 点击下载

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/127198.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年4月9,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 为什么需要持久化?
  • 你应该了解的概念
  • SpringBoot集成Quartz
    • Quartz初始化表
      • Spring整合Quartz
        • pom文件
          • 对应的properties 文件
            • 核心QuartzConfiguration类:
              • JobFactory类
                • UploadTask 类:
                  • indexController类:
                  • 总结
                  相关产品与服务
                  容器服务
                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档