SpringBoot 整合Quartz(集群)实现定时任务调度

如果对quartz集群原理和配置不太了解,大家可参考文章:Quartz集群原理及配置应用,这里我就不细说了

SpringBoot实现定时任务两种方式:

1、基于 spring的@Scheduled注解;

可参考文章:SpringBoot整合Quartz定时任务(基于注解方式),这种方式比较便捷,在单台服务部署情况下可使用,若部署多台机器,到了时间点,便会同时均开始这个执行定时任务(问题)。

2、使用Quartz(集群环境)实现:

2.1 Quzrtz集群的实现方式在于11张表,集群节点相互之间不通信,而是通过相同的数据库表来感知到另一节点的,因此Quartz集群依赖于数据库,必须先创建数据库表,数据表是官方提供的 ,有11张表,如下:

项目结构:

2.2 创建maven项目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>

    <groupId>com.test.quartz</groupId>
    <artifactId>quartz</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.7</version>
        </dependency>
        <!--spring boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <version>2.1.0.RELEASE</version>
            <exclusions>
                <exclusion>
                    <artifactId>log4j-api</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
            <version>2.1.0.RELEASE</version>
            <exclusions>
                <exclusion>
                    <artifactId>log4j-api</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-boot-starter-logging</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <!--commons-lang -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.12.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>netty</artifactId>
                    <groupId>io.netty</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>log4j</artifactId>
                    <groupId>log4j</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>guava</artifactId>
                    <groupId>com.google.guava</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.8.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.8.2</version>
        </dependency>
    </dependencies>

    <build>
        <!--<finalName>${artifactId}-${version}</finalName>-->
        <!--<plugins>-->
        <!--<plugin>-->
        <!--<groupId>org.apache.maven.plugins</groupId>-->
        <!--<artifactId>maven-compiler-plugin</artifactId>-->
        <!--<configuration>-->
        <!--<source>1.8</source>-->
        <!--<target>1.8</target>-->
        <!--</configuration>-->
        <!--</plugin>-->
        <!--</plugins>-->
        <!--打包后的jar包名-->
        <finalName>${artifactId}-${version}</finalName>
        <plugins>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.2</version>
                <executions>
                    <execution>
                        <id>prepare-agent</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>report</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>post-unit-test</id>
                        <phase>test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                        <configuration>
                            <dataFile>target/jacoco.exec</dataFile>
                        </configuration>
                    </execution>
                </executions>
                <configuration>
                    <destFile>
                        target/jacoco.exec
                    </destFile>
                </configuration>
            </plugin>
            <!-- progurad-maven-plugin -->
            <plugin>
                <groupId>com.github.wvengen</groupId>
                <artifactId>proguard-maven-plugin</artifactId>
                <version>2.0.11</version>
                <executions>
                    <execution>
                        <!--混淆阶段是package-->
                        <phase>package</phase>
                        <goals>
                            <goal>proguard</goal>
                        </goals>
                    </execution>
                </executions>
                <!--配置项-->
                <configuration>
                    <!--指定proguard的版本-->
                    <proguardVersion>6.0.3</proguardVersion>
                    <!--多模块项目打包重点一,添加依赖子模块到混淆范围,混淆后都会放在classes中-->
                    <assembly>
                        <inclusions>
                            <inclusion>
                                <groupId>${project.groupId}</groupId>
                                <artifactId>quartz</artifactId>
                            </inclusion>
                        </inclusions>
                    </assembly>
                    <!--输入的jar名-->
                    <injar>${project.build.finalName}.jar</injar>
                    <!--输出的jar名,当与injar相同时,会将混淆后的class文件放入jar中,源class文件放入项目名_proguard_base.jar中-->
                    <outjar>${project.build.finalName}.jar</outjar>
                    <!--混淆是默认开启的。混淆使类和类成员名称变成短的随机名,被各种“-keep”选项保护的类、类成员除外。对调试有用的内部属性(源文件名称,变量名称,行号)将被删除。-->
                    <obfuscate>true</obfuscate>
                    <options>
                        <!--指定java版本-->
                        <option>-target 1.8</option>
                        <!--忽略警告-->
                        <option>-ignorewarnings</option>
                        <!--排除依赖-->
                        <!-- 不做收缩(删除注释、未被引用代码)-->
                        <option>-dontshrink</option>
                        <!-- 不做优化(变更代码实现逻辑)-->
                        <option>-dontoptimize</option>
                        <!-- 确定统一的混淆类的成员名称来增加混淆-->
                        <option>-useuniqueclassmembernames</option>
                        <!--保护代码中的Annotation不被混淆,在使用如fastJSON时的实体映射-->
                        <option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
                            SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
                        </option>
                        <!--混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代-->
                        <option>-adaptclassstrings</option>
                        <!--保留启动类不混淆-->
                        <option>-keep class com.iflyrec.jk.application.TaskCenterApplication { *; }</option>
                        <!--保留实体类不混淆-->
                        <option>-keep class com.test.quartz.** { *; }</option>
                        <!--包名不混淆-->
                        <option>-keeppackagenames</option>
                        <!--参数名不混淆-->
                        <!--<option>-keepparameternames</option>-->
                        <!--getset不混淆-->
                        <!--<option>-keepclassmembers public class * {void set*(***);*** get*();}</option>-->
                        <!--<option>-keep class !com.iflyrec.jk.** {*;}</option>-->
                        <!--<option>-keepclassmembers class * {-->
                        <!--@org.springframework.beans.factory.annotation.Autowired *;-->
                        <!--@org.springframework.beans.factory.annotation.Value *;-->
                        <!--}-->
                        <!--</option>-->
                    </options>
                    <!--必要的工具包-->
                    <libs>
                        <!-- Include main JAVA library required.-->
                        <lib>${java.home}/lib/rt.jar</lib>
                        <!--<lib>${java.home}/lib/jce.jar</lib>-->
                    </libs>
                </configuration>
                <!--依赖包-->
                <dependencies>
                    <!--proguard-->
                    <dependency>
                        <groupId>net.sf.proguard</groupId>
                        <artifactId>proguard-base</artifactId>
                        <version>6.0.3</version>
                    </dependency>
                </dependencies>
            </plugin>

            <!-- Maven assembly must be run after proguard obfuscation so it take already obfuscated files.-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.0.4.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                        <configuration>
                            <!--指定主类-->
                            <mainClass>com.test.quartz.application.CenterApplication</mainClass>
                            <!--多模块项目打包重点二,需要在构建可执行jar包时去除加入混淆的子模块-->
                            <!--  <excludes>
                                  <exclude>
                                      <groupId>${project.groupId}</groupId>
                                      <artifactId>子模块名称</artifactId>
                                  </exclude>
                              </excludes>-->
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

 

2.3  创建springboot启动类(CenterApplication):

 

package com.test.quartz.application;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;

/**
 * @author xqli7@iflytek.com
 * @date 2019/2/13
 * @description: springboot启动类
 */
@SpringBootApplication
@ComponentScan(basePackages = "com.test.quartz.*")
@EnableScheduling
public class CenterApplication {
    private static final Logger LOG = LoggerFactory.getLogger(CenterApplication.class);

    private static final String START_PACKAGE = "com.test.quartz.";

    public static class CustomGenerator implements BeanNameGenerator {
        @Override
        public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) {
            String beanName = beanDefinition.getBeanClassName();
            if (beanName.startsWith(START_PACKAGE)) {
                return beanName;
            }
            try {
                Class<?> aClass = Class.forName(beanName);
                Component annotation = aClass.getAnnotation(Component.class);
                if (annotation == null) {
                    return beanName;
                }
                String value = annotation.value();
                if (StringUtils.isNotBlank(value)) {
                    return value;
                }
            } catch (Exception e) {
                LOG.error("generateBeanName method throw a exception", e);
            }
            return beanName;
        }
    }


    public static void main(String... args) {
        ApplicationContext ctx = new SpringApplicationBuilder(CenterApplication.class)
                .beanNameGenerator(new CustomGenerator())
                .run(args);
        String[] activeProfiles = ctx.getEnvironment().getActiveProfiles();
        for (String profile : activeProfiles) {
            LOG.info("Spring Boot 使用profile为: application-{}.yml", profile);
        }
    }
}


2.4   配置文件:

application.yml 

spring: 
  profiles: 
    active: win
    #active: linux

application-win.yml(window上的配置,一般喜欢将window和linux分开来写,后期部署时方便切换,liunx用不上暂时就不放了):

server:
  port: 7008

logging:
  config: classpath:properties/logback-spring.xml
  #config: classpath:properties/logback-spring.xml

charset:
  encoding: utf-8
  
spring:
  quartz:
    job-store-type: jdbc

 

日志文件logback-spring.xml ,通过在配置文件中指定生效的环境和日志的级别 :

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <root level="INFO">
        <!--<appender-ref ref="ROLLIN"/>-->
    </root>
    <logger name="org.springframework.web" level="INFO"/>
</configuration>

 

QuartzConfiguration配置文件:org.quartz.jobStore.isClustered 是否启用集群,设置为true

package com.test.quartz.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;
import java.util.Properties;

/**
 * @author xqli7@iflytek.com
 * @date 2019/2/13
 * @description: quartz配置
 */
@Configuration
public class QuartzConfiguration implements SchedulerFactoryBeanCustomizer {

    @Autowired
    DataSource dataSource;

    @Override
    public void customize(SchedulerFactoryBean schedulerFactoryBean) {
        schedulerFactoryBean.setQuartzProperties(quartzProperties());
        schedulerFactoryBean.setStartupDelay(2);
        schedulerFactoryBean.setAutoStartup(true);
        schedulerFactoryBean.setDataSource(dataSource);
        //如果这个覆盖配置为false,quratz启动以后将以数据库的数据为准,配置文件的修改不起作用。
        schedulerFactoryBean.setOverwriteExistingJobs(true);
    }

    /**
     * 设置参数
     *
     * @return quartz参数
     */
    private Properties quartzProperties() {
        Properties prop = new Properties();
        // 调度标识名 集群中每一个实例都必须使用相同的名称
        prop.put("org.quartz.scheduler.instanceName", "quartz-01");
        // ID设置为自动获取 每一个必须不同
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        // 数据保存方式为持久化
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        // 数据库方言
        prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
        // 表前缀
        prop.put("org.quartz.jobStore.tablePrefix", "t_jk_qrtz_");
        // 是否启用集群功能
        prop.put("org.quartz.jobStore.isClustered", "true");
        // 设置一个频度(毫秒),用于实例报告给集群中的其他实例。这会影响到侦测失败实例的敏捷度。它只用于设置了 isClustered 为 true 的时候。
        prop.put("org.quartz.jobStore.clusterCheckinInterval", "20000");
        // 这是 JobStore 能处理的错过触发的 Trigger 的最大数量。处理太多(超过两打) 很快会导致数据库表被锁定够长的时间,这样就妨碍了触发别的(还未错过触发) trigger 执行的性能。
        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
        // 当前时间与下一次执行的时间差大于改值时认为missFire(错过触发),根据missFire原则处理任务;若小于该值直接执行任务。默认60000(60秒)
        prop.put("org.quartz.jobStore.misfireThreshold", "120000");
        // 值为 True 时告诉 Quartz (当使用 JobStoreTX 或 CMT 时),调用 JDBC 连接的 setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE) 方法,设置事务隔离级别为串行
        // 这能助于防止某些数据库在高负荷和长事物时的锁超时。
        prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
        // 执行任务的线程池配置
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "10");
        prop.put("org.quartz.threadPool.threadPriority", "5");
        // 指定Quartz生成的线程是否继承初始化线程的上下文类加载器
        prop.put("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true");
        // 跳过更新检查
        prop.put("org.quartz.scheduler.skipUpdateCheck", true);
        //quartz 插件配置
        prop.put("org.quartz.plugin.triggHistory.class", "org.quartz.plugins.history.LoggingJobHistoryPlugin");
        prop.put("org.quartz.plugin.shutdownhook.class", "org.quartz.plugins.management.ShutdownHookPlugin");
        prop.put("org.quartz.plugin.shutdownhook.cleanShutdown", "true");
        return prop;
    }
}

 

mysql配置文件:MysqlConfig

package com.test.quartz.configuration;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

/**
 * @author xqli7@iflytek.com
 * @date 2019/2/13
 * @description: mysql连接配置
 */
@Component
public class MysqlConfig {

    @Bean
    public DataSource dataSource() {

        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/hiseejk?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai");
        hikariDataSource.setUsername("root");
        hikariDataSource.setPassword("root");
        hikariDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        hikariDataSource.setPoolName("hiseejk");
//      连接超时时间
        hikariDataSource.setConnectionTimeout(30000);
//      最大池大小
        hikariDataSource.setMaximumPoolSize(20);
//      默认连接数
        hikariDataSource.setMinimumIdle(10);
        hikariDataSource.setIdleTimeout(600000);
        hikariDataSource.setMaxLifetime(1800000);

        return hikariDataSource;
    }
}

任务实体类:

package com.test.quartz.quartz;

import java.util.Map;

/**
 * @author xqli7@iflytek.com
 * @date 2019/2/13
 * @description: 任务实体类
 */
public class JobView {
    private String name;
    private String group;
    private String cronExpression;

    private String nextFireTime;
    private String status;
    Map<String, Object> params;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGroup() {
        return group;
    }

    public void setGroup(String group) {
        this.group = group;
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
    }

    public String getNextFireTime() {
        return nextFireTime;
    }

    public void setNextFireTime(String nextFireTime) {
        this.nextFireTime = nextFireTime;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public Map<String, Object> getParams() {
        return params;
    }

    public void setParams(Map<String, Object> params) {
        this.params = params;
    }
}

 

任务工具类QuartzJobUtils:

package com.test.quartz.quartz;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.curator.shaded.com.google.common.collect.Lists;
import org.apache.curator.shaded.com.google.common.collect.Maps;
import org.apache.http.client.utils.DateUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author xqli7@iflytek.com
 * @date 2019/2/13
 * @description:
 */
public class QuartzJobUtils {
    private static final Logger LOG = LoggerFactory.getLogger(QuartzJobUtils.class);

    private QuartzJobUtils() {
        super();
    }

    /**
     * 创建quartz定时任务
     *
     * @param scheduler      调度器
     * @param jobClass       任务class,必须继承QuartzJobBean
     * @param cronExpression cron表达式
     * @param jobDataMap     任务信息
     */
    public static void createJob(Scheduler scheduler, String name, Class<? extends QuartzJobBean> jobClass, String cronExpression, JobDataMap jobDataMap) {
        //任务所属分组
        String group = "TEST";
        LOG.info("----createJob start ,name:{},group:{}", name, group);
        if (exist(scheduler, name, group)) {
            LOG.info("----createJob fail ,job already existed,name:{},group:{}", name, group);
            return;
        }
        //cron表达式封装
        //missfire处理 withMisfireHandlingInstructionDoNothing 错过触发时间时,不执行执行的,等待下一个执行时间
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
        //创建任务
        JobDetail jobDetail;
        if (jobDataMap != null) {
            //requestRecovery(true)指在集群中,一个scheduler执行job失败,将会被另外一个scheduler执行
            jobDetail = JobBuilder.newJob(jobClass).withIdentity(name, group).usingJobData(jobDataMap).requestRecovery(true).build();
        } else {
            jobDetail = JobBuilder.newJob(jobClass).withIdentity(name, group).requestRecovery(true).build();
        }
        //创建任务触发器
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group).withSchedule(scheduleBuilder).build();
        //将触发器与任务绑定到调度器内
        try {
            Date firstFireTime = scheduler.scheduleJob(jobDetail, trigger);
            LOG.info("----createJob success,firstFireTime:{}", DateFormatUtils.format(firstFireTime, "yyyy-MM-dd HH:mm:ss"));
        } catch (SchedulerException e) {
            LOG.error("-----createJob exception", e);
        }
    }

    /**
     * 创建quartz定时任务
     *
     * @param scheduler      调度器
     * @param jobClass       任务class,必须继承QuartzJobBean
     * @param cronExpression cron表达式
     */
    public static void createJob(Scheduler scheduler, String name, Class<? extends QuartzJobBean> jobClass, String cronExpression) {
        createJob(scheduler, name, jobClass, cronExpression, null);
    }

    public static void close(Scheduler scheduler, JobExecutionContext context) {
        TriggerKey triggerKey = context.getTrigger().getKey();
        JobKey jobKey = context.getJobDetail().getKey();
        LOG.info("----quartzJob close,jobName:{}", jobKey.getName());
        try {
            // 停止触发器
            scheduler.pauseTrigger(triggerKey);
            // 移除触发器
            scheduler.unscheduleJob(triggerKey);
            // 删除任务
            scheduler.deleteJob(jobKey);
            LOG.info("----quartzJob success");
        } catch (SchedulerException e) {
            LOG.error("----quartzJob close exception", e);
        }
    }

    /**
     * 判断任务是否存在
     *
     * @param scheduler 调度器
     * @param name      任务名称
     * @param group     任务分组
     * @return true==存在 false==不存在
     */
    public static boolean exist(Scheduler scheduler, String name, String group) {
        JobDetail jobDetail;
        JobKey jobKey = new JobKey(name, group);
        TriggerKey triggerKey = new TriggerKey(name, group);
        try {
            jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail != null) {
                if (Trigger.TriggerState.ERROR.equals(scheduler.getTriggerState(triggerKey))) {
                    LOG.info("-----定时任务状态异常,恢复状态,job name :{}", name);
                    scheduler.resumeJob(jobKey);
                }
                return true;
            }
        } catch (SchedulerException e) {
            LOG.error("-----exist exception", e);
        }
        return false;
    }

    /**
     * 获取定时任务列表
     *
     * @param scheduler 任务调度器
     * @return
     */
    public static List<JobView> jobs(Scheduler scheduler) {
        List<JobView> jobViews = Lists.newArrayList();
        try {
            for (String groupName : scheduler.getJobGroupNames()) {
                for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
                    String jobName = jobKey.getName();
                    String jobGroup = jobKey.getGroup();

                    // name and group
                    JobView jobView = new JobView();
                    jobView.setName(jobName);
                    jobView.setGroup(jobGroup);

                    // params
                    JobDetail qJobDetail = scheduler.getJobDetail(jobKey);
                    if (null != qJobDetail.getJobDataMap()) {
                        Map<String, Object> params = Maps.newHashMap();
                        params.putAll(qJobDetail.getJobDataMap());
                        jobView.setParams(params);
                    }
                    List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                    // 应该只有一个
                    for (Trigger trigger : triggers) {
                        trigger.getNextFireTime();
                        if (trigger instanceof CronTrigger) {
                            CronTrigger cronTrigger = (CronTrigger) trigger;
                            String cronExpr = cronTrigger.getCronExpression();
                            jobView.setCronExpression(cronExpr);
                        }
                        jobView.setNextFireTime(DateUtils.formatDate(trigger.getNextFireTime(), "yyyy-MM-dd HH:mm:ss"));
                        Trigger.TriggerState state = scheduler.getTriggerState(trigger.getKey());
                        jobView.setStatus(state.name());
                    }
                    jobViews.add(jobView);
                }
            }
        } catch (Exception e) {
            return Collections.emptyList();
        }
        return jobViews;
    }
}

 

创建具体任务类(写需要实现的一些逻辑):

package com.test.quartz.quartz;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * @author xqli7@iflytek.com
 * @date 2019/2/13
 * @description: 具体任务类
 */
@DisallowConcurrentExecution
public class TestQuartzJob extends QuartzJobBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestQuartzJob.class);

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) {
        LOGGER.info("-----TestQuartzJob start");
      //这里写具体的实现业务逻辑
        try {
         //线程睡眠是因为需要测试,防止程序过快执行
            Thread.sleep(5000);
            System.out.println("任务执行了" + "================================");

        } catch (Exception e) {
            LOGGER.error("休眠时间异常", e);
        }

    }

}

 

最后再将所有任务初始化:QuartzJobManager

package com.test.quartz.quartz;

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * @author xqli7@iflytek.com
 * @date 2019/2/13
 * @description: 初始化任务
 */
@Component
public class QuartzJobManager {
    @Autowired
    private Scheduler scheduler;


    /**
     * 初始化任务的创建
     * 判断任务是否存在,不存在则创建
     */
    @PostConstruct
    public void initJob() {
       //每分钟执行一次
        QuartzJobUtils.createJob(scheduler, TestQuartzJob.class.getSimpleName(), TestQuartzJob.class,
                "0 0/1 * * * ? ");
    }
}

执行结果:

若是部署多台机器,到了时间点,只有一台会执行,其他不会执行。(测试方案:修改tamcat端口号,启动多台服务)

若多个节点其中一个scheduler执行job失败,将会被另外一个scheduler执行。(测试方案:修改tamcat端口号,启动多台服务,将正在执行这个任务的服务关掉)

问题:

任务节点部署在不同服务器时间同步问题(暂未解决)

代码地址:https://download.csdn.net/download/qq_33914912/10957062 (积分默认的,我想改成免费的,如果有的会的教下我去改)

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值