Springboot2.X集成Quartz集羣

爲何要使用Quzrtz集羣java

  在項目進行集羣部署時,若是業務在執行中存在互斥關係,沒有對定時任務進行統一管理,就會引發業務的屢次執行,不能知足業務要求。這時就須要對任務進行管理,要保證一筆業務在全部的集羣環境中,有且只有一臺機器能執行該任務。web

  若是不適用Quartz集羣,要如何實現這種業務邏輯?redis

  在這裏只列出兩種簡單的思路:spring

    1. 利用單線程機制。能夠在redis中設置一個屬性爲空,每次任務執行時去設置這個全局變量,進入任務中須要對值進行校驗,值不爲空則跳過本次執行任務,值爲空時進行設置,方法執行完畢後再將該值置空。
    2. 指定該定時任務在某臺固定機器IP上執行,其餘Ip則跳過該任務。

  這兩種方法都有其弊端,第一種是對代碼侵入較大,第二種則是沒法作到高可用。sql

準備工做數據庫

    首先在http://www.quartz-scheduler.org/downloads/ 上下載quzrtz包,本文以quartz-2.3.0-distribution.tar.gz 版本爲例。下載後解壓,選擇你須要的數據庫進行表建立,本文以oracle爲例,顧使用tables_oracle.sql便可。tips:能夠直接在解壓後 的文件夾裏搜索table,就能找到支持的全部類型的數據庫腳本。apache

核心代碼實現tomcat

  quartz.properties 基本配置屬性mybatis

 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進行實例化和屬性設置 注意 使用集羣方案 須要鏈接數據庫,而內存版則不用配置數據庫oracle

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,主要因爲存在外鍵的緣由,因此要按順序刪除。

相關文章
相關標籤/搜索