A Code Example of Integrated Implementation of Distributed Timing Task Cluster with SpringBoot and Quartz

  • 2021-07-06 10:52:55
  • OfStack

Integration of Spring, Boot and Quartz to Realize Distributed Timing Task Cluster

Stick the code directly

POM


<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>
  <groupId>test.daemon</groupId>
  <artifactId>clusterquartz</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>clusterquartz</name>
  <url>http://maven.apache.org</url>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.1.RELEASE</version>
    <relativePath />
  </parent>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.0.13</version>
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
    </dependency>
    <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz</artifactId>
      <version>2.2.1</version>
    </dependency>
    <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz-jobs</artifactId>
      <version>2.2.1</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

application.yml


server:
 port: 80
spring:
 datasource:
  url: jdbc:mysql://localhost:3306/quartz
  username: admin
  password: admin
  driver-class-name: com.mysql.jdbc.Driver

quartz.properties


#============================================================================
# Configure JobStore
# Using Spring datasource in SchedulerConfig.java
# Spring uses LocalDataSourceJobStore extension of JobStoreCMT
#============================================================================
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 5000
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.txIsolationLevelReadCommitted = true
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#============================================================================
# Configure Main Scheduler Properties
# Needed to manage cluster instances
#============================================================================
org.quartz.scheduler.instanceName = ClusterQuartz
org.quartz.scheduler.instanceId= AUTO
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
#============================================================================
# Configure ThreadPool
# Can also be configured in spring configuration
#============================================================================
#org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#org.quartz.threadPool.threadCount = 5
#org.quartz.threadPool.threadPriority = 5
#org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

Spring Configuration Class


package test.daemon.clusterquartz.config;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.Executor;
import javax.sql.DataSource;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import test.daemon.clusterquartz.quartz.QuartzJob;
@Configuration
public class SchedulerConfig {
  @Autowired
  private DataSource dataSource;
  @Bean
  public Scheduler scheduler() throws Exception {
    Scheduler scheduler = schedulerFactoryBean().getScheduler();
    scheduler.start();
    return scheduler;
  }
  @Bean
  public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
    SchedulerFactoryBean factory = new SchedulerFactoryBean();
    factory.setSchedulerName("Cluster_Scheduler");
    factory.setDataSource(dataSource);
    factory.setApplicationContextSchedulerContextKey("applicationContext");
    factory.setTaskExecutor(schedulerThreadPool());
    factory.setTriggers(trigger1().getObject());
    factory.setQuartzProperties(quartzProperties());
    return factory;
  }
  @Bean
  public Properties quartzProperties() throws IOException {
    PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
    propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
    //  In quartz.properties After the properties in are read and injected, the object is initialized 
    propertiesFactoryBean.afterPropertiesSet();
    return propertiesFactoryBean.getObject();
  }
  @Bean
  public JobDetailFactoryBean job1() {
    JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
    jobDetailFactoryBean.setJobClass(QuartzJob.class);
    jobDetailFactoryBean.setDurability(true);
    jobDetailFactoryBean.setRequestsRecovery(true);
    return jobDetailFactoryBean;
  }
  @Bean
  public CronTriggerFactoryBean trigger1() {
    CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
    cronTriggerFactoryBean.setJobDetail(job1().getObject());
    cronTriggerFactoryBean.setCronExpression("0/3 * * * * ?");
    return cronTriggerFactoryBean;
  }
  @Bean
  public Executor schedulerThreadPool() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(15);
    executor.setMaxPoolSize(25);
    executor.setQueueCapacity(100);
    return executor;
  }
}

Class Quartz job


package test.daemon.clusterquartz.quartz;
import java.util.Date;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.scheduling.quartz.QuartzJobBean;
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class QuartzJob extends QuartzJobBean {
  @Override
  protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
    // TODO Auto-generated method stub
    System.out.println("\nQuartz job " + new Date());
  }
}

Spring Boot Startup Class


package test.daemon.clusterquartz;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Cluster {
  public static void main(String[] args) throws Exception {
    SpringApplication.run(Cluster.class, args);
  }
}

Database sql

You can find the appropriate database generation file in lib of Quartz to create the tables required by jdbc job store. These tables are used for scheduling Quartz in cluster environment.

1 Some explanations

Make a copy of the project, then change the startup port of spring server, and start multiple projects. You can observe that Quartz is running for only one project. If the server currently running Quartz hangs up, another one will follow up and perform the same Quartz task.

Part to be considered

In the Quartz cluster environment, time synchronization is an important issue, and there is time to see how to synchronize time to ensure the correctness of the cluster.

Summarize


Related articles: