Springboot集成Quartz集群

为什么要使用Quzrtz集群

  在项目进行集群部署时,如果业务在执行中存在互斥关系,没有对定时任务进行统一管理,就会引起业务的多次执行,不能满足业务要求。这时就需要对任务进行管理,要保证一笔业务在所有的集群环境中,有且只有一台机器能执行该任务。

  如果不适用Quartz集群,要如何实现这种业务逻辑?

  在这里只列出两种简单的思路:

    1. 利用单线程机制。可以在redis中设置一个属性为空,每次任务执行时去设置这个全局变量,进入任务中需要对值进行校验,值不为空则跳过本次执行任务,值为空时进行设置,方法执行完毕后再将该值置空。
    2. 指定该定时任务在某台固定机器IP上执行,其他Ip则跳过该任务。

  这两种方法都有其弊端,第一种是对代码侵入较大,第二种则是无法做到高可用。

准备工作

    首先在http://www.quartz-scheduler.org/downloads/ 上下载quzrtz包,本文以quartz-2.3.0-distribution.tar.gz 版本为例。下载后解压,选择你需要的数据库进行表创建,本文以oracle为例,顾使用tables_oracle.sql即可。tips:可以直接在解压后 的文件夹里搜索table,就能找到支持的所有类型的数据库脚本。

核心代码实现

  quartz.properties 基本配置属性

 1 ###############内存版配置####################################
 2 #org.quartz.scheduler.instanceName=spring-boot-quartz-demo
 3 #org.quartz.scheduler.instanceId=AUTO
 4 #org.quartz.threadPool.threadCount=5
 5 #org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
 6 ###############集群版配置####################################
 7 org.quartz.scheduler.instanceName=spring-boot-quartz-demo
 8 org.quartz.scheduler.instanceId=AUTO
 9 org.quartz.scheduler.skipUpdateCheck=true
10 org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
11 org.quartz.threadPool.threadCount=5
12 org.quartz.threadPool.threadPriority=5
13 org.quartz.jobStore.misfireThreshold=60000
14 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
15 #org.quartz.jobStore.dataSource=dataSource
16 org.quartz.jobStore.tablePrefix=QRTZ_
17 org.quartz.jobStore.isClustered=true
18 org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
19 org.quartz.jobStore.useProperties=false
  QuartzJobFactory.java  注入到Spring 容器
package top.enjoyitlife.config;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.stereotype.Component;

@Component
public   class QuartzJobFactory  extends SpringBeanJobFactory{
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object jobInstance = super.createJobInstance(bundle);
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

 QuartzConfig.java 配置类  加载配置文件 对quartz进行实例化和属性设置 注意 使用集群方案 需要连接数据库,而内存版则不用配置数据库

package top.enjoyitlife.config;

import java.io.IOException;
import java.util.Properties;
import javax.sql.DataSource;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
public class QuartzConfig {

    public static final String QUARTZ_PROPERTIES_PATH = "/quartz.properties";

    @Bean
    public JobFactory jobFactory(ApplicationContext applicationContext) {
        QuartzJobFactory jobFactory = new QuartzJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        return jobFactory;
    }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory,DataSource dataSource, PlatformTransactionManager transactionManager) throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setAutoStartup(true);
        factory.setJobFactory(jobFactory);
        factory.setQuartzProperties(quartzProperties());
      //集群版配置
        factory.setDataSource(dataSource);
        factory.setTransactionManager(transactionManager);
        return factory;
    }

    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource(QUARTZ_PROPERTIES_PATH));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

}

    QuartzBindOperationConfig.java 定时任务的绑定 包括 触发器  CRON表达式 备注 任务名 任务所在组

package top.enjoyitlife.config;

import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import top.enjoyitlife.schedule.TestSchedule;

@Configuration
public class QuartzBindOperationConfig {
    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private QuartzJobFactory quartzJobFactory;
    @Autowired
    private Scheduler scheduler;
    public void scheduleBind() {
        try {
            scheduler.setJobFactory(quartzJobFactory);
            JobDetail tesJobDetail = JobBuilder.newJob(TestSchedule.class)
                    .withIdentity("tesJob", "tesJob").withDescription("定时任务demo")
                    .build();
            CronScheduleBuilder tesJobCronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ?");
            CronTrigger tesJobCronTrigger = TriggerBuilder.newTrigger()
                    .withIdentity("tesJobTrigger", "tesJob")
                    .withSchedule(tesJobCronScheduleBuilder).build();
            scheduler.scheduleJob(tesJobDetail, tesJobCronTrigger);
            scheduler.start();
        } catch (SchedulerException e) {
            // e.printStackTrace();
        }

    }

}

  ApplicationListenerConfig.java 项目启动初始化即加载任务

package top.enjoyitlife.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;

@Configuration
public class ApplicationListenerConfig implements ApplicationListener<ContextRefreshedEvent> {

    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private QuartzBindOperationConfig quartzBindOperationConfig;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        quartzBindOperationConfig.scheduleBind();
    }

}

  TestSchedule.java 测试任务演示类

package top.enjoyitlife.schedule;
import java.util.Date;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@DisallowConcurrentExecution
public class TestSchedule implements Job{

     private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        logger.info("*****"+new Date());
    }}

   POM.xml 引用如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>top.enjoyitlife</groupId>
    <artifactId>enjoyitlife</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>quartzDistribute</name>
    <description>quartz集群demo</description>

    <properties>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <repositories>
        <repository>
            <id>maven-ali</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public//</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
                <updatePolicy>always</updatePolicy>
                <checksumPolicy>fail</checksumPolicy>
            </snapshots>
        </repository>
    </repositories>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

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

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

        <dependency>
            <groupId>org.codehaus.janino</groupId>
            <artifactId>janino</artifactId>
        </dependency>

        <!--  根据本地情况 进行对应调整-->
      <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.4.0</version>
         </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

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

          <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

运行日志:

2019-05-11 16:11:20.076  INFO 9624 --- [Quartz_Worker-1] top.enjoyitlife.schedule.TestSchedule    : *****Sat May 11 16:11:20 CST 2019
2019-05-11 16:11:30.037  INFO 9624 --- [Quartz_Worker-2] top.enjoyitlife.schedule.TestSchedule    : *****Sat May 11 16:11:30 CST 2019
2019-05-11 16:11:40.033  INFO 9624 --- [Quartz_Worker-3] top.enjoyitlife.schedule.TestSchedule    : *****Sat May 11 16:11:40 CST 2019

  任务执行后 数据库中就可以看到我对应的任务记录

集群效果验证

修改application.properties 中的server.port端口号,然后重新运行QuartzDistributeApplication的main 方法。以下截图为双服务同时启动时的任务,目前是只有任务1有日志记录,服务2没有任务记录。

停止任务1应用,然后我们会看到任务2 打印以下日志:

【小技巧】

  如果要手动修改集群中的任务CRON 表达式,需要首先修改表QRTZ_CRON_TRIGGERS中的CRON_EXPRESSION表达式,然后在修改表QRTZ_TRIGGERS中的NEXT_FIRE_TIME和PREV_FIRE_TIME的值为0,在下次任务执行后,该任务的执行间隔就会生效。

  如果要手动删除集群中的定时任务,删除表记录顺序如下:QRTZ_CRON_TRIGGERS----->QRTZ_TRIGGERS---->QRTZ_JOB_DETAILS,主要由于存在外键的原因,所以要按顺序删除。

原文地址:https://www.cnblogs.com/enjoyitlife/p/10840385.html

时间: 2024-10-10 08:14:22

Springboot集成Quartz集群的相关文章

项目中使用Quartz集群分享--转载

项目中使用Quartz集群分享--转载 在公司分享了Quartz,发布出来,希望大家讨论补充. CRM使用Quartz集群分享  一:CRM对定时任务的依赖与问题  二:什么是quartz,如何使用,集群,优化  三:CRM中quartz与Spring结合使用 1:CRM对定时任务的依赖与问题  1)依赖  (1)每天晚上的定时任务,通过sql脚本 + crontab方式执行 Xml代码   #crm 0 2 * * * /opt/***/javafiles/***/shell/***_dail

spring boot 整合 quartz 集群环境 实现 动态定时任务配置【原】

最近做了一个spring boot 整合 quartz  实现 动态定时任务配置,在集群环境下运行的 任务.能够对定时任务,动态的进行增删改查,界面效果图如下: 1. 在项目中引入jar 2. 将需要的表导入数据库 官网上有不同数据库的脚本,找到对应的,导入即可 3. java 代码 将quartz 的相关配置文件,配置为暴露bean,方便后期引用. 有一处关键的地方,就是注入spring 上下文,也可以算是一个坑.如果,不注入spring 上下文,那么新添加的定时任务job,是新new 的一个

spring boot + quartz 集群

spring boot bean配置: @Configuration public class QuartzConfig { @Value("${quartz.scheduler.instanceName}") private String quartzInstanceName; @Value("${org.quartz.dataSource.myDS.driver}") private String myDSDriver; @Value("${org.q

spring quartz 集群配置

Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中.它提供了巨大的灵活性而不牺牲简单性.你能够用它来为执行一个作业而创建简单的或复杂的调度. 在项目中有大量的后台任务需要调度执行,如构建索引.统计报表.周期同步数据等等,要求任务调度系统具备高可用性.负载均衡特性,使用Quartz 会很方便. 下文是spring和quartz进行整合,同时支持集群部署.quartz集群的支持是通过数据库进行任务调度的感知. 1.使用的版本情况:spring

Quartz集群配置

Quartz集群配置(100%成功) 先看看quartz的持久化基本介绍: 引用 1 大家都清楚quartz最基本的概念就是job,在job内调用具体service完成具体功能,quartz需要把每个job存储起来,方便调度,quartz存储job方式就分三种,我们最常用的也是quartz默认的是RAMJobStore,RAMJobStore顾名思义就是把job的相关信息存储在内存里,如果用spring配置quartz的job信息的话,所有信息是配置在xml里,当spirng context启动

原!总结 quartz集群 定时任务 测试运行ok

由于项目优化重构,想将定时任务从quartz单机模式变成集群或分布式的方式.于是,百度了一圈....修修改改...用集群的方式部署定时任务,测试可以... 集群?分布式?什么区别? 集群:同一个业务,部署在多个服务器上 分布式:一个业务分拆多个子业务,部署在不同的服务器上 或者说 集群:是指在多台不同的服务器中部署相同应用或服务模块,构成一个集群,通过负载均衡设备对外提供服务. 分布式:是指在多台不同的服务器中部署不同的服务模块,通过远程调用协同工作,对外提供服务. 平时常用的quartz单机模

Spring+监听器+Quartz集群(1)——基本配置

先说说项目背景,因为业务需求,项目中有很多定时任务,这自然用到了Quartz这个开源产品.而在这之前使用的Quartz主要是基于内存的,在应用程序启动时,由监听器创建定时任务,为了防止多个应用程序重新创建任务,只能在发布时,在另外的web.xml中禁用监听器.这样系统发布变得麻烦,因为不同的应用程序的配置不同. 除了发布麻烦,还有不能发挥集群的优势,一旦运行任务的服务器崩溃,集群中的其他服务器不能接受定时任务. 为了解决以上的两个问题,在系统中使用Quartz集群,同时考虑到系统中创建定时任务的

使用sqlserver搭建高可用双机热备的Quartz集群部署【附源码】

一般拿Timer和Quartz相比较的,简直就是对Quartz的侮辱,两者的功能根本就不在一个层级上,如本篇介绍的Quartz强大的集群机制,可以采用基于 sqlserver,mysql的集群方案,当然还可以在第三方插件的基础上实现quartz序列化到热炒的mongodb,redis,震撼力可想而知,接下来本篇就和大家聊 一聊怎么搭建基于sqlserver的quartz集群,实现这么一种双机热备的强大功能. 一:下载sqlserver版的建表脚本   首先大家可以通过github上搜索quart

quartz集群调度机制调研及源码分析---转载

quartz2.2.1集群调度机制调研及源码分析引言quartz集群架构调度器实例化调度过程触发器的获取触发trigger:Job执行过程:总结:附: 引言 quratz是目前最为成熟,使用最广泛的java任务调度框架,功能强大配置灵活.在企业应用中占重要地位.quratz在集群环境中的使用方式是每个企业级系统都要考虑的问题.早在2006年,在ITeye上就有一篇关于quratz集群方案的讨论:http://www.iteye.com/topic/40970 ITeye创始人@Robbin在8楼