SpringBoot 2.x quartz多資料來源定時任務配置

F嘉陽發表於2019-07-29

基礎環境

SpringBoot 2.0 開始官方提供了對quartz定時任務的自動配置支援依賴spring-boot-starter-quartz元件,無需自行整合

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
複製程式碼

環境版本:

  1. SpringBoot 2.1.6.RELEASE
  2. Java 8

定時任務配置

新增依賴

在pom.xml中新增quartz、Jpa、mysql驅動、lombok依賴


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
複製程式碼

新增配置檔案

新增資料庫配置

spring:
  datasource:
    primary:
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: hmdt
      url: jdbc:mysql://db/bussines_db?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    quartz:
      url: jdbc:mysql://db/bussines_quartz?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: hmdt

  quartz:
    #相關屬性配置
    properties:
      org:
        quartz:
          scheduler:
            instanceName: clusteredScheduler
            instanceId: AUTO
          jobStore:
            # 資料來源名稱
            dataSource: quartzDataSource
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_
            isClustered: true
            clusterCheckinInterval: 1000
            useProperties: false
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
    job-store-type: jdbc
    #初始化表結構
    jdbc:
      initialize-schema: never
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    database-platform: org.hibernate.dialect.MySQL55Dialect
    properties:
      hibernate:
        format_sql: true
        use_sql_comments: true
複製程式碼

資料庫配置也有樣例配在spring.quartz.properties中,但親測配置無效,並且由於還需要指定連線池等資訊,此處配置在spring的連線池中更容易管理

下載官網建表語句

儘管使用了JPA,但由於沒有實體關聯,spring並不會幫我們自動建表,還需要去官網下載sql指令碼

quartz官網

官網壓縮包路徑quartz-2.3.0-SNAPSHOT\src\org\quartz\impl\jdbcjobstore中支援大量資料庫建表語句,選擇對應的使用即可

1564406464554.png

配置多資料來源

多配置源之前在文章中詳細說過配置樣例和原因,這裡不再贅述

SpringBoot2.x Data JPA 多資料來源爬坑

編寫資料庫配置,統一使用HikariCP連線池

package com.gcb.invest.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

/**
 * 定時任務資料來源配置
 *
 * @author F嘉陽
 * @date 2019-07-28 21:52
 */
@Configuration
public class DataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.primary")
    public DataSourceProperties primaryDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.primary.configuration")
    public HikariDataSource firstDataSource() {
        return primaryDataSourceProperties().initializeDataSourceBuilder()
                .type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.quartz")
    public DataSourceProperties quartzDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @QuartzDataSource
    @ConfigurationProperties("spring.datasource.quartz.configuration")
    public HikariDataSource quartzDataSource() {
        return quartzDataSourceProperties().initializeDataSourceBuilder()
                .type(HikariDataSource.class).build();
    }
}
複製程式碼

定時任務配置

配置樣例1——Bean注入

編寫定時任務,由於經過Spring的封裝,使用變得更加方便

此處通過內部類配置定時任務,並使用@DisallowConcurrentExecution註解指定其在分散式環境下不可併發執行

import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * 定時任務配置
 *
 * @author :F嘉陽
 * @date :2019/7/29 9:16
 */
@Slf4j
@Configuration
public class QuartzConfig {

    /**
     * 測試定時任務構建
     *
     * @return
     */
    @Bean
    public JobDetail testTaskJobDetail() {
        return JobBuilder.newJob(TestTask.class)
                .withIdentity(TestTask.class.getName())
                .storeDurably(true)
                .build();
    }

    /**
     * 測試定時任務配置
     *
     * @return
     */
    @Bean
    public Trigger testTaskTrigger() {
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/2 * * * * ?");
        return TriggerBuilder.newTrigger()
                .forJob(testTaskJobDetail())
                .withIdentity(TestTask.class.getName())
                .withSchedule(scheduleBuilder)
                .build();
    }

    @DisallowConcurrentExecution
    private class TestTask extends QuartzJobBean {
        @Override
        protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            log.debug("執行測試定時任務");
        }
    }
}
複製程式碼

配置樣例2——排程器注入

另一種方式可使用注入排程器手動構建任務,不過需要呼叫該方法之後排程器才會被建立

import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.UUID;

/**
 * @author :F嘉陽
 * @date :2019/7/29 8:59
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class QuartzTimerService {

    @Autowired
    private Scheduler scheduler;

    public void buildGoodStockTimer() throws Exception {
        //任務名稱
        String name = UUID.randomUUID().toString();
        //任務所屬分組
        String group = CustomQuartzJob.class.getName();

        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ?");
        //建立任務
        JobDetail jobDetail = JobBuilder.newJob(CustomQuartzJob.class).withIdentity(name,group).build();
        //建立任務觸發器
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(name,group).withSchedule(scheduleBuilder).build();
        //將觸發器與任務繫結到排程器內
        scheduler.scheduleJob(jobDetail, trigger);
    }
}
複製程式碼

定時任務類

import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * @author :F嘉陽
 * @date :2019/7/29 8:55
 */
@Slf4j
@DisallowConcurrentExecution
public class CustomQuartzJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("執行定時任務");
    }
}
複製程式碼

執行結果

執行成功

1564383676571.png

在分散式環境下自動進行作業節點遷移

1564383705389.png

相關文章