使用quartz实现可以后台配置的定时任务服务

1 篇文章 0 订阅

创建工程

创建common-api工程

pom.xml

<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.wl.common</groupId>
  <artifactId>scheduler-api</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>scheduler-api</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <!-- 打包源码插件-->
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-source-plugin</artifactId>
        <executions>
          <execution>
            <id>attach-sources</id>
            <goals>
              <goal>jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <!-- 指定maven 打包java 版本 -->
      <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
      </plugin>
    </plugins>
  </build>

</project>

存储定时任务信息的实体vo对象 TaskVo

package com.wl.common.scheduler.api.model;

import java.io.Serializable;

/**
 * Created by Administrator on 2020/7/12.
 */
public class TaskVo implements Serializable {

    private Integer id;   //id

    private String name;  //任务名称

    private String description; //任务描述

    private String cronTriggerText;  //触发器描述

    private String cronTriggerExpression;  //触发表达式

    private String jobName;     //定时任务名

    private String jobGroup;     //定时任务组

    private String jobClass;     //定时任务对应的Job实现类的全路径

    private String prevFireTime;     //上次执行时间

    private String thisFireTime;     //本次执行时间

    private String nextFireTime;     //下次执行时间

    private String startTime;        //开始执行时间

    private String endTime;          //任务结束时间  为空 不结束

    private Integer exceptionCount;  //异常次数

    private Integer executedCount;   //执行次数

    private Integer stateType;       //状态

    private String stateTypeDesc;    //状态描述

    private String createTime;      //创建时间

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getCronTriggerText() {
        return cronTriggerText;
    }

    public void setCronTriggerText(String cronTriggerText) {
        this.cronTriggerText = cronTriggerText;
    }

    public String getCronTriggerExpression() {
        return cronTriggerExpression;
    }

    public void setCronTriggerExpression(String cronTriggerExpression) {
        this.cronTriggerExpression = cronTriggerExpression;
    }

    public String getJobName() {
        return jobName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    public String getJobGroup() {
        return jobGroup;
    }

    public void setJobGroup(String jobGroup) {
        this.jobGroup = jobGroup;
    }

    public String getJobClass() {
        return jobClass;
    }

    public void setJobClass(String jobClass) {
        this.jobClass = jobClass;
    }

    public String getPrevFireTime() {
        return prevFireTime;
    }

    public void setPrevFireTime(String prevFireTime) {
        this.prevFireTime = prevFireTime;
    }

    public String getThisFjiekireTime() {
        return thisFireTime;
    }

    public void setThisFireTime(String thisFireTime) {
        this.thisFireTime = thisFireTime;
    }

    public String getNextFireTime() {
        return nextFireTime;
    }

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

    public String getStartTime() {
        return startTime;
    }

    public void setStartTime(String startTime) {
        this.startTime = startTime;
    }

    public String getEndTime() {
        return endTime;
    }

    public void setEndTime(String endTime) {
        this.endTime = endTime;
    }

    public Integer getExceptionCount() {
        return exceptionCount;
    }

    public void setExceptionCount(Integer exceptionCount) {
        this.exceptionCount = exceptionCount;
    }

    public Integer getExecutedCount() {
        return executedCount;
    }

    public void setExecutedCount(Integer executedCount) {
        this.executedCount = executedCount;
    }

    public Integer getStateType() {
        return stateType;
    }

    public void setStateType(Integer stateType) {
        this.stateType = stateType;
    }

    public String getStateTypeDesc() {
        return stateTypeDesc;
    }

    public void setStateTypeDesc(String stateTypeDesc) {
        this.stateTypeDesc = stateTypeDesc;
    }

    public String getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }

    public enum StateType implements Serializable{
        NOT_EXECUTE("未执行"),  //未执行
        RUNNING("正在运行"),     //正在运行
        PAUSE("暂停"),       //暂停
        EXCEPTION("异常"),    //异常
        EXPIRED("过期")
        ;
        public final Integer VALUE;
        public final String DESC;

        StateType(String desc){
            this.VALUE = this.ordinal();
            this.DESC = desc;
        }
    }
}

接口

package com.wl.common.scheduler.api.interfaces;

import com.wl.common.scheduler.api.model.PageVo;
import com.wl.common.scheduler.api.model.TaskVo;

/**
 * Created by Administrator on 2020/7/12.
 */
public interface ISchedulerService {

    //新增任务
    TaskVo addScheduler(TaskVo taskVo) throws Exception;

    //更新任务
    TaskVo updateScheduler(TaskVo taskVo) throws Exception;

    //删除任务
    void deleteScheduler(TaskVo taskVo) throws Exception;

    //分页查询
    PageVo<TaskVo> querySchedulerWithPage(Integer pageNum, Integer pageSize);

    //启动定时任务
    void startJob(TaskVo taskVo) throws Exception;

    //暂停定时任务
    void stopJob(TaskVo taskVo) throws Exception;
    
	//重启定时任务
    void resumeJob(TaskVo taskVo) throws Exception;
}

创建scheduler-service工程

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.wl.service.scheduler</groupId>
  <artifactId>scheduler-service</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>scheduler-service</name>


  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring-boot-version>1.5.7.RELEASE</spring-boot-version>
    <quartz-version>2.3.0</quartz-version>
    <mysql-connector-java-version>6.0.6</mysql-connector-java-version>
    <druid-version>1.1.6</druid-version>
    <hibernate-core-version>5.2.10.Final</hibernate-core-version>

    <dubbo-version>2.6.0</dubbo-version>
    <zookeeper-version>3.4.10</zookeeper-version>

    <slf4j-api-version>1.7.5</slf4j-api-version>
    <commons-lang-version>3.6</commons-lang-version>
    <junit-version>4.12</junit-version>
  </properties>

  <dependencies>

    <dependency>
      <groupId>com.wl.common</groupId>
      <artifactId>scheduler-api</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>${spring-boot-version}</version>
      <exclusions>
        <exclusion>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>4.3.11.RELEASE</version>
    </dependency>
    
    <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz</artifactId>
      <version>${quartz-version}</version>
      <exclusions>
        <exclusion>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz-jobs</artifactId>
      <version>${quartz-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
      <version>${spring-boot-version}</version>
      <exclusions>
        <exclusion>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-core</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-entitymanager</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate-core-version}</version>
      <exclusions>
        <exclusion>
          <groupId>com.fasterxml</groupId>
          <artifactId>classmate</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql-connector-java-version}</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>${druid-version}</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>${dubbo-version}</version>
      <exclusions>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-beans</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>${commons-lang-version}</version>
    </dependency>

    <dependency>
      <groupId>org.apache.zookeeper</groupId>
      <artifactId>zookeeper</artifactId>
      <version>${zookeeper-version}</version>
      <exclusions>
        <exclusion>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
        </exclusion>

        <exclusion>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
        </exclusion>

        <exclusion>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>com.github.sgroschupf</groupId>
      <artifactId>zkclient</artifactId>
      <version>0.1</version>
      <exclusions>
        <exclusion>
          <groupId>org.apache.zookeeper</groupId>
          <artifactId>zookeeper</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j-api-version}</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit-version}</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <version>${spring-boot-version}</version>
      <scope>test</scope>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>${spring-boot-version}</version>
        <configuration>
          <mainClass>com.wl.service.scheduler.Application</mainClass>
          <layout>JAR</layout>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

目录结构

scheduler-service的实现

项目目录结构
在这里插入图片描述

scheduler-service的默认配置

application.properties(这里不贴logger的配置,如果不需要logger 请注释掉logging.config)

quartz.enabled = true
spring.application.name=scheduler-service
#logger
logging.config=classpath:logger/logback-boot.xml

# http port
#server.port=9001
#dubbo
dubbo.port=20212
dubbo.host=127.0.0.1
#zookeeper
env.host.zookeeper=192.168.92.128
zookeeper.address=zookeeper://${env.host.zookeeper}:2181?timeout=20000

#spring.main.web-environment=false

#db
env.host.db=127.0.0.1

spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc\:mysql\://${env.host.db}\:3306/scheduler?useUnicode\=true&characterEncoding\=UTF-8&&useSSL=false&autoReconnect=true&failOverReadOnly=false&maxReconnects=20&serverTimezone=GMT%2B8 
spring.datasource.username=root
spring.datasource.password=root


#Specify the DBMS
spring.jpa.database = MYSQL
#Show or not log for each sql query
spring.jpa.show-sql = true
#Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = none
#Naming strategy
spring.jpa.hibernate.naming.strategy= org.hibernate.cfg.ImprovedNamingStrategy
#stripped before adding them to the entity manager
#spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5Dialect

dubbo.xml (不使用dubbo调用微服务可以暂时不用)

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!--半自动化,使用annotation来注解-->
    <dubbo:application name="scheduler-service"/>
    <!-- 使用zookeeper广播注册中心暴露服务地址 -->
    <dubbo:registry protocol="zookeeper" address="${zookeeper.address}"/>
    <!-- 扫描注解包路径,多个包用逗号分隔,不填pacakge表示扫描当前ApplicationContext中所有的类 -->
    <dubbo:annotation package="com.wl"/>
    <!--这里可以声明多种协议,rest的协议应该使用内嵌的tomcat-->
    <dubbo:protocol name="dubbo" port="${dubbo.port}" host="${dubbo.host}"/>
    <dubbo:consumer check="false" version="1.0.0" retries="0"/>
    <dubbo:provider version="1.0.0" retries="0"/>
    <dubbo:monitor protocol="registry"/>
</beans>

DubboConfig.java(这里先注释掉)

package com.wl.service.scheduler.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

/**
 * Created by Administrator on 2020/7/12.
 */
@Configuration
@ImportResource(locations = "classpath:dubbo/dubbo.xml")
public class DubboConfig {
    
}

scheduler-service的quartz配置

quartz.properties文件(与application.properties放在同一目录)

#============================================================================
# Configure Main Scheduler Properties
#============================================================================

org.quartz.scheduler.instanceId = AUTO
org.quartz.scheduler.makeSchedulerThreadDaemon = true

#============================================================================
# Configure ThreadPool
#============================================================================

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.makeThreadsDaemons = true
org.quartz.threadPool.threadCount: 20
org.quartz.threadPool.threadPriority: 5

#============================================================================
# Configure JobStore
#============================================================================

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.dataSource = quartzDataSource
org.quartz.jobStore.misfireThreshold = 25000


##============================================================================
## Configure Datasources
##============================================================================
#
org.quartz.dataSource.quartzDataSource.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.quartzDataSource.URL = jdbc:mysql://localhost:3306/scheduler?useUnicode\=true&characterEncoding\=UTF-8&&useSSL=false&autoReconnect=true&failOverReadOnly=false&maxReconnects=20&serverTimezone=GMT%2B8
org.quartz.dataSource.quartzDataSource.user = root
org.quartz.dataSource.quartzDataSource.password = root
org.quartz.dataSource.quartzDataSource.maxConnections = 25
org.quartz.dataSource.quartzDataSource.validationQuery = select 0

org.quartz.dataSource.quartzDataSource.connectionProvider.class = com.wl.service.scheduler.quartz.connection.QuartzConnectionProvider

注意org.quartz.threadPool.threadCount这个配置限制了定时任务的数量,如果定时任务较多需要增加这个配置的数量
org.quartz.dataSource.quartzDataSource.connectionProvider.class 配置QuartzConnectionProvider.java(配置quartz的数据库连接)

package com.wl.service.scheduler.quartz.connection;


import com.alibaba.druid.pool.DruidDataSource;
import org.quartz.utils.ConnectionProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * Created by wl on 2018/3/28.
 */
@Component
public class QuartzConnectionProvider implements ConnectionProvider{

    private static final Logger logger = LoggerFactory.getLogger(QuartzConnectionProvider.class);

    private DruidDataSource dataSource;   //com.alibaba.druid.pool.DruidDataSource

    //db properties
    //JDBC驱动
    private String driver;
    //JDBC连接串
    private String URL;
    //数据库用户名
    private String user;
    //数据库用户密码
    private String password;
    //数据库最大连接数
    private int maxConnections;
    //数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。
    private String validationQuery;

    private boolean validateOnCheckout;

    private int idleConnectionValidationSeconds;

    private String maxCachedStatementsPerConnection;

    private String discardIdleConnectionsSeconds;

//    public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;

    private static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;

    /**
     * @return connection managed by this provider
     */
    public Connection getConnection() throws SQLException {

        return dataSource.getConnection();
    }

    public void shutdown() throws SQLException {
        dataSource.close();
    }

    public void initialize() throws SQLException {
        dataSource = new DruidDataSource();
        dataSource.setUrl(this.URL);
        dataSource.setUsername(this.user);
        dataSource.setPassword(this.password);
        dataSource.setMaxActive(this.maxConnections);
        dataSource.setMinIdle(1);
        dataSource.setMaxWait(0);
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(this.DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION);
        if (this.validationQuery != null) {
            dataSource.setValidationQuery(this.validationQuery);
            if(!this.validateOnCheckout)
                dataSource.setTestOnReturn(true);
            else
                dataSource.setTestOnBorrow(true);
            dataSource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);
        }
    }


    public DruidDataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DruidDataSource dataSource) {
        this.dataSource = dataSource;
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getURL() {
        return URL;
    }

    public void setURL(String URL) {
        this.URL = URL;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getMaxConnections() {
        return maxConnections;
    }

    public void setMaxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
    }

    public String getValidationQuery() {
        return validationQuery;
    }

    public void setValidationQuery(String validationQuery) {
        this.validationQuery = validationQuery;
    }

    public boolean isValidateOnCheckout() {
        return validateOnCheckout;
    }

    public void setValidateOnCheckout(boolean validateOnCheckout) {
        this.validateOnCheckout = validateOnCheckout;
    }

    public int getIdleConnectionValidationSeconds() {
        return idleConnectionValidationSeconds;
    }

    public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {
        this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;
    }

    public String getMaxCachedStatementsPerConnection() {
        return maxCachedStatementsPerConnection;
    }

    public void setMaxCachedStatementsPerConnection(String maxCachedStatementsPerConnection) {
        this.maxCachedStatementsPerConnection = maxCachedStatementsPerConnection;
    }

    public String getDiscardIdleConnectionsSeconds() {
        return discardIdleConnectionsSeconds;
    }

    public void setDiscardIdleConnectionsSeconds(String discardIdleConnectionsSeconds) {
        this.discardIdleConnectionsSeconds = discardIdleConnectionsSeconds;
    }
}

QuartzConfig.java(注入SchedulerFactoryBean以及quartz.properties)

package com.wl.service.scheduler.config;

import org.quartz.SchedulerContext;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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.scheduling.quartz.SpringBeanJobFactory;

import java.io.IOException;
import java.util.Properties;

/**
 * Created by wl on 2018/3/28.
 */
@Configuration
@ConditionalOnProperty(name = "quartz.enabled")
public class QuartzConfig {

    @Bean
    public JobFactory jobFactory() {
        SpringBeanJobFactory jobFactory = new SpringBeanJobFactory();
        jobFactory.setSchedulerContext(new SchedulerContext());
        return jobFactory;
    }

    @Bean
    @Autowired
    public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) throws Exception {

        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        schedulerFactoryBean.setAutoStartup(true);
        schedulerFactoryBean.setJobFactory(jobFactory);
        schedulerFactoryBean.setQuartzProperties(quartzProperties());
        return schedulerFactoryBean;
    }


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

}

scheduler-service 中的数据库表

quartz自带的table

-- #
-- # Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
-- #
-- # PLEASE consider using mysql with innodb tables to avoid locking issues
-- #
-- # In your Quartz properties file, you'll need to set
-- # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-- #

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;


CREATE TABLE QRTZ_JOB_DETAILS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CRON_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(200) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (          
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_BLOB_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CALENDARS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME  VARCHAR(200) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP  VARCHAR(200) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(200) NULL,
    JOB_GROUP VARCHAR(200) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);

CREATE TABLE QRTZ_SCHEDULER_STATE
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME  VARCHAR(40) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);


commit;

自定义保存定时任务信息的表task 最好job_name与job_group 建立唯一索引

CREATE TABLE `task` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `description` varchar(150) DEFAULT NULL,
  `cron_trigger_text` varchar(150) DEFAULT NULL COMMENT '触发器描述',
  `cron_trigger_expression` varchar(50) DEFAULT NULL COMMENT '触发器表达式',
  `job_name` varchar(100) NOT NULL COMMENT '任务名称',
  `job_group` varchar(100) NOT NULL COMMENT '任务组',
  `job_class` varchar(200) NOT NULL COMMENT '任务对应的Job实现类的全路径',
  `prev_fire_time` datetime DEFAULT NULL COMMENT '上次执行时间',
  `this_fire_time` datetime DEFAULT NULL COMMENT '本次执行时间',
  `next_fire_time` datetime DEFAULT NULL COMMENT '下次执行时间',
  `start_time` datetime DEFAULT NULL COMMENT '开始执行时间',
  `end_time` datetime DEFAULT NULL COMMENT '任务结束时间(为空不结束)',
  `exception_count` int(11) DEFAULT NULL COMMENT '执行异常次数',
  `executed_count` int(11) DEFAULT NULL COMMENT '执行次数',
  `state_type` tinyint(4) NOT NULL COMMENT '状态',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

quartz的新增 更新 启动 暂停接口的实现

下面直接贴代码,主要是task的数据库增删改查、org.quartz.Scheduler的启动、暂停、删除任务以及任务的监听三块
common 接口的实现
SchedulerServiceImpl.java

package com.wl.service.scheduler.impl;

import com.wl.common.scheduler.api.interfaces.ISchedulerService;
import com.wl.common.scheduler.api.model.PageVo;
import com.wl.common.scheduler.api.model.TaskVo;
import com.wl.service.scheduler.model.Task;
import com.wl.service.scheduler.quartz.util.SchedulerConvert;
import com.wl.service.scheduler.service.SchedulerService;
import com.wl.service.scheduler.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

/**
 * Created by Administrator on 2020/7/15.
 */
@Service
public class SchedulerServiceImpl implements ISchedulerService {

    private SchedulerService schedulerService;

    private TaskService taskService;

    @Autowired
    public SchedulerServiceImpl(SchedulerService schedulerService,
                                TaskService taskService){
        this.schedulerService = schedulerService;
        this.taskService = taskService;
    }

    public TaskVo addScheduler(TaskVo taskVo) throws Exception {
        return schedulerService.addScheduler(taskVo);
    }

    public TaskVo updateScheduler(TaskVo taskVo) throws Exception {
        return schedulerService.updateScheduler(taskVo);
    }

    public void deleteScheduler(TaskVo taskVo) throws Exception {
        schedulerService.deleteScheduler(taskVo);
    }

    public PageVo<TaskVo> querySchedulerWithPage(Integer pageNum, Integer pageSize) {
        PageVo<TaskVo> pageVo = new PageVo<>();
        Page<Task> page = taskService.querySchedulerWithPage(pageNum,pageSize);
        if(page != null){
            pageVo.setPageNum(page.getNumber());
            pageVo.setPageSize(page.getSize());
            pageVo.setTotalPages(page.getTotalPages());
            pageVo.setTotalElements(page.getTotalElements());
            pageVo.setData(SchedulerConvert.convertTaskList2Vo(page.getContent()));
        }
        return pageVo;
    }

    public void startJob(TaskVo taskVo) throws Exception {
        schedulerService.startTask(taskVo);
    }

    public void stopJob(TaskVo taskVo) throws Exception {
        schedulerService.stopJob(taskVo);
    }
    
    @Override
    public void resumeJob(TaskVo taskVo) throws Exception {
        schedulerService.resumeJob(taskVo);
    }
}

SchedulerService.java

package com.wl.service.scheduler.service;

import com.wl.common.scheduler.api.model.TaskVo;
import com.wl.service.scheduler.model.Task;
import com.wl.service.scheduler.quartz.util.SchedulerConvert;
import com.wl.service.scheduler.quartz.util.SchedulerUtil;
import com.wl.service.scheduler.repository.ITaskRepository;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Created by Administrator on 2020/7/15.
 */
@Service
@Transactional(readOnly = true)
public class SchedulerService {

    private SchedulerUtil schedulerUtil;       //操作schedule

    private ITaskRepository iTaskRepository;   //操作数据库

    @Autowired
    public SchedulerService(SchedulerUtil schedulerUtil,ITaskRepository iTaskRepository){
        this.schedulerUtil = schedulerUtil;
        this.iTaskRepository = iTaskRepository;
    }

    @Transactional(rollbackFor = Exception.class)
    public TaskVo addScheduler(TaskVo taskVo) throws SchedulerException {

        Task task = SchedulerConvert.convertTaskVo2Task(taskVo);

        iTaskRepository.save(task);
//        int i = 1/0;
        //将任务加入 schedule
        addTask(taskVo);

        return taskVo;
    }

    @Transactional(rollbackFor = Exception.class)
    public TaskVo updateScheduler(TaskVo taskVo) throws SchedulerException {
        //入库
        Task task = SchedulerConvert.convertTaskVo2Task(taskVo);
        //状态与之前一致
        iTaskRepository.save(task);

        //加入schedule  无论是否已经存在
        addTask(taskVo);

        //判断状态  正在运行
        if(TaskVo.StateType.RUNNING.VALUE.equals(taskVo.getStateType())){
            //开始任务   reSchedule
            startTask(taskVo);
        }

        return taskVo;
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteScheduler(TaskVo taskVo) throws SchedulerException {
        //更改TaskInfo 状态    不存在
        iTaskRepository.updateTaskStatus(taskVo.getJobName(),taskVo.getJobGroup(),TaskVo.StateType.DELETE.VALUE);
        //删除任务
        deleteTask(taskVo.getJobName(),taskVo.getJobGroup());

    }

    @Transactional(rollbackFor = Exception.class)
    public void stopJob(TaskVo taskVo) throws SchedulerException {
        //更改状态
        iTaskRepository.updateTaskStatus(taskVo.getJobName(),taskVo.getJobGroup(),TaskVo.StateType.PAUSE.VALUE);
        //停止任务  (删除trigger  || 删除job)
        schedulerUtil.stopTask(taskVo);
    }

    //开始执行任务
    public void startTask(TaskVo taskVo) throws SchedulerException {
        //
        schedulerUtil.startTask(taskVo);
    }

    //将任务加入schedule(不执行)
    private void addTask(TaskVo taskVo) throws SchedulerException {
        schedulerUtil.addTask(taskVo);
    }

    //删除job
    private void deleteTask(String jobName, String jobGroup) throws SchedulerException {
        schedulerUtil.deleteTask(jobName,jobGroup);
    }

    //判断job是否存在
    public boolean isTaskExist(String jobName, String jobGroup) throws SchedulerException {
        return schedulerUtil.isTaskExist(jobName,jobGroup);
    }
    
	public void resumeJob(TaskVo taskVo) throws SchedulerException {
        schedulerUtil.resumeJob(taskVo);
    }

}

TaskService.java

package com.wl.service.scheduler.service;

import com.wl.service.scheduler.model.Task;
import com.wl.service.scheduler.repository.ITaskRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * Created by Administrator on 2020/7/15.
 */
@Service
@Transactional(readOnly = true)
public class TaskService {

    private ITaskRepository iTaskRepository;

    @Autowired
    public TaskService(ITaskRepository iTaskRepository){
        this.iTaskRepository = iTaskRepository;
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateTaskStatusAndFireTime(
            String jobName,
            String jobGroup,
            Date prevTime,
            Date thisTime,
            Date nextTime,
            Integer status) {
        iTaskRepository.updateTaskStatusAndFireTime(jobName,jobGroup,prevTime,thisTime,nextTime,status);
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateTaskStatusAndFireTimeAddExecuteCount(
            String jobName,
            String jobGroup,
            Date prevTime,
            Date thisTime,
            Date nextTime,
            Integer status) {
        iTaskRepository.updateTaskStatusAndFireTimeAddExecuteCount(jobName,jobGroup,prevTime,thisTime,nextTime,status);
    }

    public Page<Task> querySchedulerWithPage(Integer pageNum, Integer pageSize) {
        return iTaskRepository.findAll(new PageRequest(pageNum - 1,pageSize));
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateTaskStatus(String jobName, String jobGroup, Integer status) {
        iTaskRepository.updateTaskStatus(jobName,jobGroup,status);
    }
}

SchedulerUtil.java

package com.wl.service.scheduler.quartz.util;

import com.wl.common.scheduler.api.model.TaskVo;
import com.wl.service.scheduler.quartz.listener.QuartzJobListener;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * Created by Administrator on 2020/7/16.
 */
@Component
public class SchedulerUtil {

    private static final Logger logger = LoggerFactory.getLogger(SchedulerUtil.class);

    private static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    private Scheduler scheduler;

    @Autowired
    public SchedulerUtil(SchedulerFactoryBean schedulerFactoryBean,
                         QuartzJobListener quartzJobListener) throws SchedulerException {
        this.scheduler = schedulerFactoryBean.getScheduler();
        this.scheduler.getListenerManager().addJobListener(quartzJobListener);
    }

    public void addTask(TaskVo taskVo) throws SchedulerException {
        Class<? extends Job> clazz;
        try {
            clazz = (Class<? extends Job>) Class.forName(taskVo.getJobClass());
        } catch (ClassNotFoundException e) {
            //
            throw new RuntimeException("");
        }
        JobDetail jobDetail = JobBuilder.
                newJob(clazz).
                storeDurably().    //Jobs added with no trigger must be durable.   没有trigger 保存
                withIdentity(JobKey.jobKey(taskVo.getJobName(),taskVo.getJobGroup())).
                withDescription(taskVo.getDescription()).
                build();


        //加入schedule
        logger.info("add job to schedule jobName:{} jobGroup:{}",taskVo.getJobName(),taskVo.getJobGroup());
        scheduler.addJob(jobDetail,true);
    }


    public void stopTask(TaskVo taskVo) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(taskVo.getJobName(),taskVo.getJobGroup());
        scheduler.pauseJob(jobKey);
    }


    public void startTask(TaskVo taskVo) throws SchedulerException {

        logger.info("execute job jobName:{} jobGroup:{}" , taskVo.getJobName(),taskVo.getJobGroup());
        //移除trigger
        //triggerKey
        TriggerKey triggerKey = TriggerKey.triggerKey(taskVo.getJobName(),taskVo.getJobGroup());
        scheduler.unscheduleJob(triggerKey);    //移除trigger  because one already exists with this identification
        //
        //cronTrigger
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.
                cronSchedule(taskVo.getCronTriggerExpression()).
                withMisfireHandlingInstructionDoNothing();
        //开始时间
        Date startTime = null;
        if(StringUtils.isNotBlank(taskVo.getStartTime())){
            startTime = SchedulerConvert.convertStr2Date(taskVo.getStartTime(),YYYY_MM_DD_HH_MM_SS);
        }
        if(StringUtils.isNotBlank(taskVo.getNextFireTime())){
            startTime = SchedulerConvert.convertStr2Date(taskVo.getNextFireTime(),YYYY_MM_DD_HH_MM_SS);
        }
        //开始时间为空 设置为当前时间
        if(startTime == null){
            startTime = new Date();
        }
        //结束时间
        Date endTime = null;
        if(StringUtils.isNotBlank(taskVo.getEndTime())){
            endTime = SchedulerConvert.convertStr2Date(taskVo.getEndTime(),YYYY_MM_DD_HH_MM_SS);
            if(endTime != null && endTime.getTime() < System.currentTimeMillis()){
                logger.error("截止时间:{}小于当前时间",taskVo.getEndTime());
            }
        }
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().
                withSchedule(cronScheduleBuilder)
                .startAt(startTime)
                .endAt(endTime)
                .forJob(scheduler.getJobDetail(JobKey.jobKey(taskVo.getJobName(),taskVo.getJobGroup())))
                .withIdentity(triggerKey)
                .withDescription(taskVo.getDescription()).
                        build();

        scheduler.scheduleJob(cronTrigger);
        //执行任务   because one already exists with this identification
//        scheduler.scheduleJob(
//                scheduler.getJobDetail(JobKey.jobKey(taskInfoFlowVo.getJobName(),taskInfoFlowVo.getJobGroup())),
//                cronTrigger);
    }

    public void deleteTask(String jobName, String jobGroup) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
        scheduler.deleteJob(jobKey);
    }


    public boolean isTaskExist(String jobName, String jobGroup) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
        return scheduler.checkExists(jobKey);
    }
    
    public void resumeJob(TaskVo taskVo) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(taskVo.getJobName(),taskVo.getJobGroup());
        scheduler.resumeJob(jobKey);
    }
}

QuartzJobListener.java 监听器

package com.wl.service.scheduler.quartz.listener;

import com.wl.common.scheduler.api.model.TaskVo;
import com.wl.service.scheduler.service.TaskService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.JobListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * Created by Administrator on 2020/7/16.
 */
@Component
public class QuartzJobListener implements JobListener{
    private static final Logger logger = LoggerFactory.getLogger(QuartzJobListener.class);

    private TaskService taskService;   //this 依赖 scheduleService 依赖 scheduleUtil 依赖 this 死循环

    @Autowired
    public QuartzJobListener(TaskService taskService){
        this.taskService = taskService;
    }

    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    /**
     * Scheduler 在 JobDetail 将要被执行时调用这个方法。
     */
    @Override
    public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
        JobKey jobKey = jobExecutionContext.getJobDetail().getKey();
        String jobName = jobKey.getName();
        String jobGroup = jobKey.getGroup();
        logger.info("任务 job:{} group:{},即将执行\n",jobName,jobGroup);
        //修改执行状态和执行时间
        Date prevTime = jobExecutionContext.getPreviousFireTime();
        Date thisTime = jobExecutionContext.getScheduledFireTime();
        Date nextTime = jobExecutionContext.getNextFireTime();
        taskService.updateTaskStatusAndFireTime(
                jobName,
                jobGroup,
                prevTime,
                thisTime,
                nextTime,
                TaskVo.StateType.RUNNING.VALUE);
    }
    /**
     * Scheduler 在 JobDetail 即将被执行,但又被 TriggerListener 否决了时调用这个方法。
     */
    @Override
    public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
        JobKey jobKey = jobExecutionContext.getJobDetail().getKey();
        String jobName = jobKey.getName();
        String jobGroup = jobKey.getGroup();
        logger.info("任务 job:{} group:{} 被TriggerListener 否决\n ",jobName,jobGroup);
        Date preTime = jobExecutionContext.getPreviousFireTime();
        taskService.updateTaskStatusAndFireTime(
                jobName,
                jobGroup,
                preTime,
                null,
                null,
                TaskVo.StateType.PAUSE.VALUE
        );
    }

    /**
     * Scheduler 在 JobDetail 被执行之后调用这个方法。
     *
     * 增加任务执行次数
     *
     */
    @Override
    public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
        JobKey jobKey = jobExecutionContext.getJobDetail().getKey();
        String jobName = jobKey.getName();
        String jobGroup = jobKey.getGroup();
        logger.info("任务 job:{} group:{} 执行完毕\n",jobName,jobGroup);
        Date prevTime =  jobExecutionContext.getPreviousFireTime();
        Date thisTime = jobExecutionContext.getFireTime();
        Date nextTime = jobExecutionContext.getNextFireTime();
        taskService.updateTaskStatusAndFireTimeAddExecuteCount(
                jobName,
                jobGroup,
                prevTime,
                thisTime,
                nextTime,
                TaskVo.StateType.RUNNING.VALUE);

    }
}

实体类 Task.java

package com.wl.service.scheduler.model;

import javax.persistence.*;
import java.util.Date;

/**
 * Created by Administrator on 2020/7/15.
 */
@Entity
@Table(name = "task")
public class Task {

    private Integer id;   //id

    private String name;  //任务名称

    private String description; //任务描述

    private String cronTriggerText;  //触发器描述

    private String cronTriggerExpression;  //触发表达式

    private String jobName;     //定时任务名

    private String jobGroup;     //定时任务组

    private String jobClass;     //对应Job实现类的全路径

    //==============================job执行后 回调参数  listener

    private Date prevFireTime;     //上次执行时间

    private Date thisFireTime;    //当前执行时间

    private Date nextFireTime;     //下次执行时间

    private Date startTime;        //开始执行时间

    private Date endTime;          //任务结束时间  为空 不结束

    private Integer exceptionCount;  //异常次数

    private Integer executedCount;   //执行次数

    private Integer stateType;       //状态

    private Date createTime;         //创建时间

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name = "name")
    public String getName() {
        return name;
    }

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

    @Column(name = "description")
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Column(name = "cron_trigger_text")
    public String getCronTriggerText() {
        return cronTriggerText;
    }

    public void setCronTriggerText(String cronTriggerText) {
        this.cronTriggerText = cronTriggerText;
    }

    @Column(name = "cron_trigger_expression")
    public String getCronTriggerExpression() {
        return cronTriggerExpression;
    }

    public void setCronTriggerExpression(String cronTriggerExpression) {
        this.cronTriggerExpression = cronTriggerExpression;
    }

    @Column(name = "job_name")
    public String getJobName() {
        return jobName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    @Column(name = "job_group")
    public String getJobGroup() {
        return jobGroup;
    }

    public void setJobGroup(String jobGroup) {
        this.jobGroup = jobGroup;
    }

    @Column(name = "job_class")
    public String getJobClass() {
        return jobClass;
    }

    public void setJobClass(String jobClass) {
        this.jobClass = jobClass;
    }

    @Column(name = "prev_fire_time")
    public Date getPrevFireTime() {
        return prevFireTime;
    }

    public void setPrevFireTime(Date prevFireTime) {
        this.prevFireTime = prevFireTime;
    }

    @Column(name = "this_fire_time")
    public Date getThisFireTime() {
        return thisFireTime;
    }

    public void setThisFireTime(Date thisFireTime) {
        this.thisFireTime = thisFireTime;
    }

    @Column(name = "next_fire_time")
    public Date getNextFireTime() {
        return nextFireTime;
    }

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

    @Column(name = "start_time")
    public Date getStartTime() {
        return startTime;
    }

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

    @Column(name = "end_time")
    public Date getEndTime() {
        return endTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    @Column(name = "exception_count")
    public Integer getExceptionCount() {
        return exceptionCount;
    }

    public void setExceptionCount(Integer exceptionCount) {
        this.exceptionCount = exceptionCount;
    }

    @Column(name = "executed_count")
    public Integer getExecutedCount() {
        return executedCount;
    }

    public void setExecutedCount(Integer executedCount) {
        this.executedCount = executedCount;
    }

    @Column(name = "state_type")
    public Integer getStateType() {
        return stateType;
    }

    public void setStateType(Integer stateType) {
        this.stateType = stateType;
    }


    @Column(name = "create_time")
    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}

ITaskRepository.java

package com.wl.service.scheduler.repository;

import com.wl.service.scheduler.model.Task;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import java.util.Date;

/**
 * Created by Administrator on 2020/7/16.
 */
public interface ITaskRepository extends JpaRepository<Task,Integer>,JpaSpecificationExecutor<Task> {

    @Modifying
    @Query("update Task o set o.stateType=?3 where o.jobName=?1 and o.jobGroup=?2")
    void updateTaskStatus(String jobName, String jobGroup, Integer value);

    @Modifying
    @Query("update Task o set o.prevFireTime = ?3 ,o.thisFireTime=?4,o.nextFireTime=?5,o.stateType=?6 where o.jobName=?1 and o.jobGroup=?2")
    void updateTaskStatusAndFireTime(String jobName, String jobGroup, Date prevTime, Date thisTime, Date nextTime, Integer status);

    @Modifying
    @Query("update Task o set o.prevFireTime = ?3 ,o.thisFireTime=?4,o.nextFireTime=?5,o.stateType=?6 ,o.executedCount=o.executedCount+1 where o.jobName=?1 and o.jobGroup=?2")
    void updateTaskStatusAndFireTimeAddExecuteCount(String jobName, String jobGroup, Date prevTime, Date thisTime, Date nextTime, Integer status);

}

ApplicationBeanUtil.java

package com.wl.service.scheduler.quartz.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * Created by wl on 2018/4/2.
 */
@Component
public class ApplicationBeanUtil implements ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(ApplicationBeanUtil.class);

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationBeanUtil.applicationContext = applicationContext;
    }
    //
    public static <T> T getBean(Class<T> t){
        T bean = null;
        if(applicationContext != null) {
            try {
                bean = applicationContext.getBean(t);
            }catch (BeansException e){
                logger.error("通过类型获取bean失败",e);
            }
            if (bean == null) {
                bean = getBean(getSimpleClassName(t.getSimpleName()),t);
            }
        }
        return bean;
    }

    //
    public static  <T> T getBean(String name,Class<T> t){
        if(applicationContext != null) {
            try {
                return applicationContext.getBean(name, t);
            }catch (BeansException e){
                logger.error("通过类名和类型获取bean失败",e);
            }
        }
        return null;
    }

    private static String getSimpleClassName(String simpleName) {
        return (simpleName.substring(0,1)).toLowerCase() + simpleName.substring(1);
    }

//    public static void main(String[] args) {
//        System.out.println(ApplicationBeanUtil.getSimpleClassName(ApplicationBeanUtil.class.getSimpleName()));
//    }

}

SchedulerConvert.java

package com.wl.service.scheduler.quartz.util;

import com.wl.common.scheduler.api.model.TaskVo;
import com.wl.service.scheduler.model.Task;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by Administrator on 2020/7/16.
 */
public class SchedulerConvert {

    private static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    public static Task convertTaskVo2Task(TaskVo taskVo) {

        Task task = new Task();

        {
            task.setId(taskVo.getId());
            task.setName(taskVo.getName());
            task.setDescription(taskVo.getDescription());
            task.setCronTriggerText(taskVo.getCronTriggerText());
            task.setCronTriggerExpression(taskVo.getCronTriggerExpression());
            task.setJobName(taskVo.getJobName());
            task.setJobGroup(taskVo.getJobGroup());
            task.setJobClass(taskVo.getJobClass());

            task.setPrevFireTime(convertStr2Date(taskVo.getPrevFireTime(), YYYY_MM_DD_HH_MM_SS));
            task.setNextFireTime(convertStr2Date(taskVo.getNextFireTime(), YYYY_MM_DD_HH_MM_SS));
            task.setThisFireTime(convertStr2Date(taskVo.getThisFireTime(),YYYY_MM_DD_HH_MM_SS));
            task.setStartTime(convertStr2Date(taskVo.getStartTime(), YYYY_MM_DD_HH_MM_SS));
            task.setEndTime(convertStr2Date(taskVo.getEndTime(),YYYY_MM_DD_HH_MM_SS));

            task.setExceptionCount(taskVo.getExceptionCount());
            task.setExecutedCount(taskVo.getExecutedCount());
            task.setStateType(taskVo.getStateType());
            task.setCreateTime(convertStr2Date(taskVo.getCreateTime(),YYYY_MM_DD_HH_MM_SS));
        }

        return task;
    }


    public static Date convertStr2Date(String dateStr, String datePattern){
        if(StringUtils.isNotBlank(dateStr)){
            try {
                return DateUtils.parseDate(dateStr,datePattern);
            } catch (ParseException e) {
                //
            }
        }
        return null;
    }

    public static List<TaskVo> convertTaskList2Vo(List<Task> taskList) {

        if(taskList != null && !taskList.isEmpty()){
            List<TaskVo> taskVos = new ArrayList<>();
            for(Task task : taskList){
                TaskVo taskVo = new TaskVo();

                {
                    taskVo.setId(task.getId());
                    taskVo.setName(task.getName());
                    taskVo.setDescription(task.getDescription());
                    taskVo.setCronTriggerText(task.getCronTriggerText());
                    taskVo.setCronTriggerExpression(task.getCronTriggerExpression());
                    taskVo.setJobName(task.getJobName());
                    taskVo.setJobGroup(task.getJobGroup());
                    taskVo.setJobClass(task.getJobClass());

                    taskVo.setPrevFireTime(convertDate2Str(task.getPrevFireTime(), YYYY_MM_DD_HH_MM_SS));
                    taskVo.setNextFireTime(convertDate2Str(task.getNextFireTime(), YYYY_MM_DD_HH_MM_SS));
                    taskVo.setThisFireTime(convertDate2Str(task.getThisFireTime(),YYYY_MM_DD_HH_MM_SS));
                    taskVo.setStartTime(convertDate2Str(task.getStartTime(), YYYY_MM_DD_HH_MM_SS));
                    taskVo.setEndTime(convertDate2Str(task.getEndTime(),YYYY_MM_DD_HH_MM_SS));

                    taskVo.setExceptionCount(task.getExceptionCount());
                    taskVo.setExecutedCount(task.getExecutedCount());
                    taskVo.setStateType(task.getStateType());

                    taskVo.setCreateTime(convertDate2Str(task.getCreateTime(),YYYY_MM_DD_HH_MM_SS));
                    taskVos.add(taskVo);
                }
            }
            return taskVos;
        }
        return null;

    }

    private static String convertDate2Str(Date date,String datePattern){
        if(date != null){
            return DateFormatUtils.format(date,datePattern);
        }
        return "";
    }
}

启动类Application.java

package com.wl.service.scheduler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;

import java.util.concurrent.CountDownLatch;

/**
 * Hello world!
 *
 */
@SpringBootApplication(scanBasePackages = "com.wl")
@EntityScan("com.wl")
public class Application {

    private static final Logger logger = LoggerFactory.getLogger(Application.class);
    public static void main( String[] args ) throws InterruptedException {
        SpringApplication application = new SpringApplication(Application.class);
        application.setWebEnvironment(false);
        application.run(args);
        logger.info("init SchedulerApplication Success");
        new CountDownLatch(1).await();
    }
}

下面是测试
新建一个OrderJob.java任务

package com.wl.service.scheduler.quartz.job;

import com.wl.common.scheduler.api.model.TaskVo;
import com.wl.service.scheduler.quartz.util.ApplicationBeanUtil;
import com.wl.service.scheduler.service.TaskService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by Administrator on 2020/7/16.
 */
public class OrderJob implements Job{
    private Logger logger = LoggerFactory.getLogger(OrderJob.class);

    private TaskService taskService;

    public OrderJob(){
        this.taskService = ApplicationBeanUtil.getBean(TaskService.class);
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobKey jobKey = context.getJobDetail().getKey();
        String jobName = jobKey.getName(), jobGroup = jobKey.getGroup();
        logger.info("更改订单状态定时任务执行");
        //更改任务执行状态为正在执行
        if(taskService != null) {
            taskService.updateTaskStatus(jobName, jobGroup, TaskVo.StateType.RUNNING.VALUE);
        }
    }
}

测试代码如下

package com.wl.service.scheduler;

import com.wl.common.scheduler.api.model.TaskInfoFlowVo;
import com.wl.common.scheduler.api.model.TaskVo;
import com.wl.service.scheduler.impl.SchedulerServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Created by Administrator on 2020/7/16.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class TestApplication {

    @Autowired
    private SchedulerServiceImpl schedulerService;

    @Test
    public void test() throws Exception {
        TaskVo taskVo = new TaskVo();
        taskVo.setId(1);
//        taskVo.setEndTime("2018-06-30 17:59:00");
        //初始化执行次数
        taskVo.setExecutedCount(0);
        taskVo.setExceptionCount(0);
        taskVo.setCronTriggerExpression("0/10 * * * * ? ");
        taskVo.setCronTriggerText("每十秒运行一次");
        taskVo.setDescription("更改订单状态定时任务");
        taskVo.setStateType(TaskInfoFlowVo.StateType.NOT_EXECUTE.VALUE);
        
        taskVo.setJobClass("com.wl.service.scheduler.quartz.job.OrderJob");
        taskVo.setJobGroup("订单");
        taskVo.setJobName("更改订单状态");
        taskVo.setName("更改订单状态定时任务");
        taskVo.setCreateTime("2020-07-16 17:59:00");

        schedulerService.addScheduler(taskVo);

        schedulerService.startJob(taskVo);

        Thread.sleep(10000000L);
    }
}

注意执行次数的初始化为0、jobClass 为Job的实现类的全路径
下面是执行日志

2020-07-16 11:58:56,931 INFO (StartupInfoLogger.java:57)- Started TestApplication in 3.934 seconds (JVM running for 4.548)
Hibernate: select task0_.id as id1_0_0_, task0_.create_time as create_t2_0_0_, task0_.cron_trigger_expression as cron_tri3_0_0_, task0_.cron_trigger_text as cron_tri4_0_0_, task0_.description as descript5_0_0_, task0_.end_time as end_time6_0_0_, task0_.exception_count as exceptio7_0_0_, task0_.executed_count as executed8_0_0_, task0_.job_class as job_clas9_0_0_, task0_.job_group as job_gro10_0_0_, task0_.job_name as job_nam11_0_0_, task0_.name as name12_0_0_, task0_.next_fire_time as next_fi13_0_0_, task0_.prev_fire_time as prev_fi14_0_0_, task0_.start_time as start_t15_0_0_, task0_.state_type as state_t16_0_0_, task0_.this_fire_time as this_fi17_0_0_ from task task0_ where task0_.id=?
Hibernate: insert into task (create_time, cron_trigger_expression, cron_trigger_text, description, end_time, exception_count, executed_count, job_class, job_group, job_name, name, next_fire_time, prev_fire_time, start_time, state_type, this_fire_time) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2020-07-16 11:58:57,034 INFO (SchedulerUtil.java:51)- add job to schedule jobName:更改订单状态 jobGroup:订单
2020-07-16 11:58:57,052 INFO (SchedulerUtil.java:64)- execute job jobName:更改订单状态 jobGroup:订单
2020-07-16 11:59:00,016 INFO (QuartzJobListener.java:43)- 任务 job:更改订单状态 group:订单,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 11:59:00,043 INFO (OrderJob.java:29)- 更改订单状态定时任务执行
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 11:59:00,046 INFO (QuartzJobListener.java:87)- 任务 job:更改订单状态 group:订单 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
2020-07-16 11:59:10,012 INFO (QuartzJobListener.java:43)- 任务 job:更改订单状态 group:订单,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 11:59:10,020 INFO (OrderJob.java:29)- 更改订单状态定时任务执行
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 11:59:10,028 INFO (QuartzJobListener.java:87)- 任务 job:更改订单状态 group:订单 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
Disconnected from the target VM, address: '127.0.0.1:63761', transport: 'socket'

task表数据如下

{
"RECORDS":[
{
"id":1,
"name":"更改订单状态定时任务",
"description":"更改订单状态定时任务",
"cron_trigger_text":"每十秒运行一次",
"cron_trigger_expression":"0\/10 * * * * ? ",
"job_name":"更改订单状态",
"job_group":"订单",
"job_class":"com.wl.service.scheduler.quartz.job.OrderJob",
"prev_fire_time":"2020\/7\/16 11:59:00",
"this_fire_time":"2020\/7\/16 11:59:00",
"next_fire_time":"2020\/7\/16 11:59:20",
"start_time":null,
"end_time":null,
"exception_count":0,
"executed_count":2,
"state_type":1,
"create_time":"2020\/7\/16 17:59:00"
}
]
}

下面我们测试停止任务 直接resume然后停止定时任务在重启(重启服务后quartz也会自动的启动之前运行的定时任务:可以在QuartzConfig.java SchedulerFactoryBean 中配置不自动启动)

@Autowired
    private ITaskRepository iTaskRepository;

    @Test
    public void testStopJob() throws Exception {
        Task task = iTaskRepository.findOne(1);
        TaskVo taskVo = SchedulerConvert.convertTaskList2Vo(Collections.singletonList(task)).get(0);
        System.out.println("重启:");
        schedulerService.resumeJob(taskVo);
        Thread.sleep(30000L);

        System.out.println("暂停");
        schedulerService.stopJob(taskVo);

        Thread.sleep(40000L);
        System.out.println("重启");
        schedulerService.resumeJob(taskVo);

        Thread.sleep(30000L);
    }

日志如下

2020-07-16 13:56:16,391 INFO (SchedulerFactoryBean.java:645)- Starting Quartz Scheduler now
2020-07-16 13:56:16,408 INFO (DruidDataSource.java:922)- {dataSource-1} inited
2020-07-16 13:56:16,444 INFO (JobStoreSupport.java:3629)- ClusterManager: detected 1 failed or restarted instances.
2020-07-16 13:56:16,445 INFO (JobStoreSupport.java:3488)- ClusterManager: Scanning for instance "ILML1N09A7L7UC31594878612915"'s failed in-progress jobs.
2020-07-16 13:56:16,453 INFO (QuartzScheduler.java:547)- Scheduler schedulerFactoryBean_$_ILML1N09A7L7UC31594878975262 started.
2020-07-16 13:56:16,457 INFO (JobStoreSupport.java:973)- Handling 1 trigger(s) that missed their scheduled fire-time.
2020-07-16 13:56:16,463 INFO (StartupInfoLogger.java:57)- Started TestApplication in 3.929 seconds (JVM running for 4.558)
Hibernate: select task0_.id as id1_0_0_, task0_.create_time as create_t2_0_0_, task0_.cron_trigger_expression as cron_tri3_0_0_, task0_.cron_trigger_text as cron_tri4_0_0_, task0_.description as descript5_0_0_, task0_.end_time as end_time6_0_0_, task0_.exception_count as exceptio7_0_0_, task0_.executed_count as executed8_0_0_, task0_.job_class as job_clas9_0_0_, task0_.job_group as job_gro10_0_0_, task0_.job_name as job_nam11_0_0_, task0_.name as name12_0_0_, task0_.next_fire_time as next_fi13_0_0_, task0_.prev_fire_time as prev_fi14_0_0_, task0_.start_time as start_t15_0_0_, task0_.state_type as state_t16_0_0_, task0_.this_fire_time as this_fi17_0_0_ from task task0_ where task0_.id=?
重启:
2020-07-16 13:56:20,018 INFO (QuartzJobListener.java:43)- 任务 job:更改订单状态 group:订单,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 13:56:20,034 INFO (OrderJob.java:29)- 更改订单状态定时任务执行
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 13:56:20,038 INFO (QuartzJobListener.java:87)- 任务 job:更改订单状态 group:订单 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
2020-07-16 13:56:30,012 INFO (QuartzJobListener.java:43)- 任务 job:更改订单状态 group:订单,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 13:56:30,016 INFO (OrderJob.java:29)- 更改订单状态定时任务执行
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 13:56:30,020 INFO (QuartzJobListener.java:87)- 任务 job:更改订单状态 group:订单 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
2020-07-16 13:56:40,011 INFO (QuartzJobListener.java:43)- 任务 job:更改订单状态 group:订单,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 13:56:40,020 INFO (OrderJob.java:29)- 更改订单状态定时任务执行
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 13:56:40,025 INFO (QuartzJobListener.java:87)- 任务 job:更改订单状态 group:订单 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
暂停
Hibernate: update task set state_type=? where job_name=? and job_group=?
重启
2020-07-16 13:57:30,016 INFO (QuartzJobListener.java:43)- 任务 job:更改订单状态 group:订单,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 13:57:30,020 INFO (OrderJob.java:29)- 更改订单状态定时任务执行
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 13:57:30,023 INFO (QuartzJobListener.java:87)- 任务 job:更改订单状态 group:订单 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
2020-07-16 13:57:40,011 INFO (QuartzJobListener.java:43)- 任务 job:更改订单状态 group:订单,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 13:57:40,015 INFO (OrderJob.java:29)- 更改订单状态定时任务执行
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 13:57:40,019 INFO (QuartzJobListener.java:87)- 任务 job:更改订单状态 group:订单 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
2020-07-16 13:57:50,007 INFO (QuartzJobListener.java:43)- 任务 job:更改订单状态 group:订单,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 13:57:50,012 INFO (OrderJob.java:29)- 更改订单状态定时任务执行
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 13:57:50,015 INFO (QuartzJobListener.java:87)- 任务 job:更改订单状态 group:订单 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
2020-07-16 13:57:56,638 INFO (AbstractApplicationContext.java:984)- Closing org.springframework.web.context.support.GenericWebApplicationContext@7e276594: startup date [Thu Jul 16 13:56:12 CST 2020]; root of context hierarchy
2020-07-16 13:57:56,639 INFO (DefaultLifecycleProcessor.java:358)- Stopping beans in phase 2147483647
2020-07-16 13:57:56,640 INFO (QuartzScheduler.java:585)- Scheduler schedulerFactoryBean_$_ILML1N09A7L7UC31594878975262 paused.
2020-07-16 13:57:56,641 INFO (SchedulerFactoryBean.java:765)- Shutting down Quartz Scheduler
2020-07-16 13:57:56,641 INFO (QuartzScheduler.java:666)- Scheduler schedulerFactoryBean_$_ILML1N09A7L7UC31594878975262 shutting down.
2020-07-16 13:57:56,641 INFO (QuartzScheduler.java:585)- Scheduler schedulerFactoryBean_$_ILML1N09A7L7UC31594878975262 paused.
2020-07-16 13:57:56,643 INFO (DruidDataSource.java:1817)- {dataSource-1} closed
2020-07-16 13:57:56,643 INFO (QuartzScheduler.java:740)- Scheduler schedulerFactoryBean_$_ILML1N09A7L7UC31594878975262 shutdown complete.
2020-07-16 13:57:56,643 INFO (AbstractEntityManagerFactoryBean.java:548)- Closing JPA EntityManagerFactory for persistence unit 'default'
Disconnected from the target VM, address: '127.0.0.1:64758', transport: 'socket'

这样我们就实现了一个可以后台配置的定时任务服务,不过这个服务存在一个问题,就是我们每次新增一个定时任务都必须要添加一个对应Job的实现类,并且都必须重新发布这个服务,这样就会导致我们正常运行的定时任务不得不在我们重新发布服务的期间停止运行

添加定时任务不用重新发布服务的实现方法

可不可以新增一个定时任务又不用重新发布服务呢?答案是可以的,如果我们想要新增一个定时任务而且又不用重新发布服务,那么显然我们是不能发布服务后添加Job的实现的,即只有一个或多个公共的Job实现类。除此之外,定时任务的业务逻辑也只能在外部服务中实现,提供给定时任务服务调用。因此每一个任务必须要存放调用外部服务的参数

quartz的特性为我们提供了这样的方便
首先quartz定时任务执行时都会重新实例化Job的实现类,因此我们完全可以使用一个公共的Job实现
其次quartz中的JobDataMap为我们提供了保存个性化参数的便利(每一个JobName、JobGroup对应的Job都有一个JobDataMap,可以实现更多个性化需求)

现在我们只需要在外部服务实现我们的定时任务业务逻辑,然后在定时任务中调用就可以实现我们上面的需求了,下面介绍使用Http、dubbo、spring-cloud 的实现方法

使用Http调用外部业务逻辑

1.scheduler-service 的pom.xml中添加http-client 依赖

<dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.6</version>
    </dependency>

2.task表中添加http_detail字段 保存http请求需要的url、parameter、等json信息(这里我就不贴Task.java以及SchedulerConvert的代码了)
3.添加HttpDetail.java对应task表中的http_detail json 数据(属性根据需求可以自定义)

package com.wl.service.scheduler.quartz.util;

import java.util.Map;

/**
 * Created by Administrator on 2020/7/16.
 */
public class HttpDetail {

    //请求url
    private String url;

    //请求参数
    private Map<String,String> param;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Map<String, String> getParam() {
        return param;
    }

    public void setParam(Map<String, String> param) {
        this.param = param;
    }
}

4.在SchedulerUtil的addTask方法中将参数存入JobDataMap中

public void addTask(TaskVo taskVo) throws SchedulerException {
        Class<? extends Job> clazz;
        try {
            clazz = (Class<? extends Job>) Class.forName(taskVo.getJobClass());
        } catch (ClassNotFoundException e) {
            //
            throw new RuntimeException("");
        }
        JobDetail jobDetail = JobBuilder.
                newJob(clazz).
                storeDurably().    //Jobs added with no trigger must be durable.   没有trigger 保存
                withIdentity(JobKey.jobKey(taskVo.getJobName(),taskVo.getJobGroup())).
                withDescription(taskVo.getDescription()).
                build();
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        if(StringUtils.isNotBlank(taskVo.getHttpDetail())) {
            try {
                HttpDetail httpDetail = new ObjectMapper().readValue(taskVo.getHttpDetail(), HttpDetail.class);
                jobDataMap.put("url", httpDetail.getUrl());
                jobDataMap.put("param", httpDetail.getParam());
            } catch (IOException e) {
                //
            }
        }
        //加入schedule
        logger.info("add job to schedule jobName:{} jobGroup:{}",taskVo.getJobName(),taskVo.getJobGroup());
        scheduler.addJob(jobDetail,true);
    }

5.新增HttpCommonJob.java

package com.wl.service.scheduler.quartz.job;

import com.wl.common.scheduler.api.model.TaskVo;
import com.wl.service.scheduler.quartz.util.ApplicationBeanUtil;
import com.wl.service.scheduler.service.TaskService;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Map;

/**
 * Created by Administrator on 2020/7/16.
 */
public class HttpCommonJob implements Job {

    private static HttpClient client = HttpClientBuilder.create().build();

    private Logger logger = LoggerFactory.getLogger(HttpCommonJob.class);

    private TaskService taskService;

    public HttpCommonJob(){
        this.taskService = ApplicationBeanUtil.getBean(TaskService.class);
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobKey jobKey = context.getJobDetail().getKey();
        String jobName = jobKey.getName(), jobGroup = jobKey.getGroup();
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        //调用服务
        String url = jobDataMap.get("url").toString();
        Map<String,String> param = jobDataMap.get("param") != null ? (Map<String, String>) jobDataMap.get("param") : null;

        try {
            URIBuilder builder = new URIBuilder(url);
            if(param != null && !param.isEmpty()){
                for(Map.Entry<String,String> entry :param.entrySet()){
                    builder.addParameter(entry.getKey(),entry.getValue());
                }
            }
            HttpGet get = new HttpGet(builder.build());
            HttpResponse response = client.execute(get);
            logger.info(EntityUtils.toString(response.getEntity()));
        } catch (URISyntaxException | IOException e) {
            //
        }

        //更改任务执行状态为正在执行
        if(taskService != null) {
            taskService.updateTaskStatus(jobName, jobGroup, TaskVo.StateType.RUNNING.VALUE);
        }
    }
}

为了方便测试我们修改服务为web应用

package com.wl.service.scheduler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CountDownLatch;

/**
 * Hello world!
 *
 */
@SpringBootApplication(scanBasePackages = "com.wl")
@EntityScan("com.wl")
@RestController
public class Application {

    private static final Logger logger = LoggerFactory.getLogger(Application.class);
    public static void main( String[] args ) throws InterruptedException {
        SpringApplication application = new SpringApplication(Application.class);
        application.setWebEnvironment(true);
        application.run(args);
        logger.info("init SchedulerApplication Success");
//        new CountDownLatch(1).await();
    }

    @RequestMapping("/httpJob")
    public String httpJob(){
        return "httpJob";
    }
}

测试代码如下

@Test
    public void testHttpJob() throws Exception {
        TaskVo taskVo = new TaskVo();
        taskVo.setId(2);
//        taskVo.setEndTime("2018-06-30 17:59:00");
        //初始化执行次数
        taskVo.setExecutedCount(0);
        taskVo.setExceptionCount(0);
        taskVo.setCronTriggerExpression("0/10 * * * * ? ");
        taskVo.setCronTriggerText("每十秒运行一次");
        taskVo.setDescription("通用http定时任务");
        taskVo.setStateType(TaskInfoFlowVo.StateType.NOT_EXECUTE.VALUE);

        taskVo.setJobClass("com.wl.service.scheduler.quartz.job.HttpCommonJob");
        taskVo.setJobGroup("httpJob");
        taskVo.setJobName("通用httpJob1");
        taskVo.setName("通用httpJob1");
        taskVo.setCreateTime("2020-07-16 17:59:00");
        HttpDetail httpDetail = new HttpDetail();
        httpDetail.setUrl("http://localhost:8080/httpJob");
        taskVo.setHttpDetail(new ObjectMapper().writeValueAsString(httpDetail));

        schedulerService.addScheduler(taskVo);

        schedulerService.startJob(taskVo);

        Thread.sleep(10000000L);

由于测试时不能访问controller的requestMapping路径,我们执行测试方法后直接关闭,然后直接启动Application 看看效果(quartz会自动启动)

2020-07-16 15:50:53,680 INFO (StartupInfoLogger.java:57)- Started Application in 3.11 seconds (JVM running for 3.406)
2020-07-16 15:50:53,680 INFO (Application.java:28)- init SchedulerApplication Success
2020-07-16 15:51:00,183 INFO (QuartzJobListener.java:43)- 任务 job:通用httpJob1 group:httpJob,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 15:51:00,271 INFO (DirectJDKLog.java:179)- Initializing Spring FrameworkServlet 'dispatcherServlet'
2020-07-16 15:51:00,272 INFO (FrameworkServlet.java:489)- FrameworkServlet 'dispatcherServlet': initialization started
2020-07-16 15:51:00,288 INFO (FrameworkServlet.java:508)- FrameworkServlet 'dispatcherServlet': initialization completed in 16 ms
2020-07-16 15:51:00,317 INFO (HttpCommonJob.java:53)- httpJob
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 15:51:00,321 INFO (QuartzJobListener.java:87)- 任务 job:通用httpJob1 group:httpJob 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
2020-07-16 15:51:10,021 INFO (QuartzJobListener.java:43)- 任务 job:通用httpJob1 group:httpJob,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 15:51:10,038 INFO (HttpCommonJob.java:53)- httpJob
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 15:51:10,042 INFO (QuartzJobListener.java:87)- 任务 job:通用httpJob1 group:httpJob 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
Disconnected from the target VM, address: '127.0.0.1:49791', transport: 'socket'

可以看到访问到了localhost:8080/httpJob

task记录如下

{
"id":2,
"name":"通用httpJob1",
"description":"通用http定时任务",
"cron_trigger_text":"每十秒运行一次",
"cron_trigger_expression":"0\/10 * * * * ? ",
"job_name":"通用httpJob1",
"job_group":"httpJob",
"job_class":"com.wl.service.scheduler.quartz.job.HttpCommonJob",
"http_detail":null,
"prev_fire_time":"2020\/7\/16 15:51:00",
"this_fire_time":"2020\/7\/16 15:51:00",
"next_fire_time":"2020\/7\/16 15:51:20",
"start_time":null,
"end_time":null,
"exception_count":0,
"executed_count":4,
"state_type":1,
"create_time":"2020\/7\/16 17:59:00"
}

使用dubbo调用外部业务逻辑(泛化调用)

1.将DubboConfig.java的注释解除
2.数据库表task中添加dubbo_detail字段(Task.java SchedulerConvert.java等需要相应的添加)
3.新增DubboDetail.java(根据需求可以自定义属性)

package com.wl.service.scheduler.quartz.util;

/**
 * Created by Administrator on 2020/7/16.
 */
public class DubboDetail {

    //服务接口
    private String serviceInterface;

    //服务组
    private String serviceGroup;

    //服务版本
    private String serviceVersion;

    //接口方法名称
    private String serviceMethod;

    //方法参数
    private String serviceMethodParameter;

    //是否异步调用 默认否
    private String serviceMethodAsync;

    //是否有返回值
    private String serviceMethodReturn;

    public String getServiceInterface() {
        return serviceInterface;
    }

    public void setServiceInterface(String serviceInterface) {
        this.serviceInterface = serviceInterface;
    }

    public String getServiceGroup() {
        return serviceGroup;
    }

    public void setServiceGroup(String serviceGroup) {
        this.serviceGroup = serviceGroup;
    }

    public String getServiceVersion() {
        return serviceVersion;
    }

    public void setServiceVersion(String serviceVersion) {
        this.serviceVersion = serviceVersion;
    }

    public String getServiceMethod() {
        return serviceMethod;
    }

    public void setServiceMethod(String serviceMethod) {
        this.serviceMethod = serviceMethod;
    }

    public String getServiceMethodParameter() {
        return serviceMethodParameter;
    }

    public void setServiceMethodParameter(String serviceMethodParameter) {
        this.serviceMethodParameter = serviceMethodParameter;
    }

    public String getServiceMethodAsync() {
        return serviceMethodAsync;
    }

    public void setServiceMethodAsync(String serviceMethodAsync) {
        this.serviceMethodAsync = serviceMethodAsync;
    }

    public String getServiceMethodReturn() {
        return serviceMethodReturn;
    }

    public void setServiceMethodReturn(String serviceMethodReturn) {
        this.serviceMethodReturn = serviceMethodReturn;
    }
}

4.将参数保存到JobDataMap中

public void addTask(TaskVo taskVo) throws SchedulerException {
        Class<? extends Job> clazz;
        try {
            clazz = (Class<? extends Job>) Class.forName(taskVo.getJobClass());
        } catch (ClassNotFoundException e) {
            //
            throw new RuntimeException("");
        }
        JobDetail jobDetail = JobBuilder.
                newJob(clazz).
                storeDurably().    //Jobs added with no trigger must be durable.   没有trigger 保存
                withIdentity(JobKey.jobKey(taskVo.getJobName(),taskVo.getJobGroup())).
                withDescription(taskVo.getDescription()).
                build();
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        if(StringUtils.isNotBlank(taskVo.getHttpDetail())) {
            try {
                HttpDetail httpDetail = new ObjectMapper().readValue(taskVo.getHttpDetail(), HttpDetail.class);
                jobDataMap.put("url", httpDetail.getUrl());
                jobDataMap.put("param", httpDetail.getParam());
            } catch (IOException e) {
                //
            }
        }
        if(StringUtils.isNotBlank(taskVo.getDubboDetail())){
            try {
                DubboDetail dubboDetail = new ObjectMapper().readValue(taskVo.getDubboDetail(),DubboDetail.class);
                jobDataMap.put("serviceInterface", dubboDetail.getServiceInterface());
                jobDataMap.put("serviceGroup", dubboDetail.getServiceGroup());
                jobDataMap.put("serviceVersion", dubboDetail.getServiceVersion());
                jobDataMap.put("serviceMethod", dubboDetail.getServiceMethod());
                jobDataMap.put("serviceMethodParameter", dubboDetail.getServiceMethodParameter());
                jobDataMap.put("serviceMethodAsync", Boolean.valueOf(dubboDetail.getServiceMethodAsync()));
                jobDataMap.put("serviceMethodReturn", Boolean.valueOf(dubboDetail.getServiceMethodReturn()));
            } catch (IOException e) {
                //
            }
        }
        //加入schedule
        logger.info("add job to schedule jobName:{} jobGroup:{}",taskVo.getJobName(),taskVo.getJobGroup());
        scheduler.addJob(jobDetail,true);
    }

5.添加测试的IDubboJobInterface以及其实现类(接口方法中有一个String 参数 为了 GenericService 调用方法Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException 的统一)

package com.wl.service.scheduler.dubbo;

/**
 * Created by Administrator on 2020/7/16.
 */
public interface IDubboJobInterface {

    String dubboJob(String str);
}

package com.wl.service.scheduler.dubbo;

import com.alibaba.dubbo.config.annotation.Service;

/**
 * Created by Administrator on 2020/7/16.
 */
@Service(protocol = "dubbo",version = "1.0.0",group = "dubboJob")
public class DubboJobMicroService implements IDubboJobInterface{

   @Override
    public String dubboJob(String str) {
        System.out.println("======================================" + str);
        return str;
    }

}

6.添加DubboCommonJob.java(使用dubbo 的泛化调用)

package com.wl.service.scheduler.quartz.job;

import com.alibaba.dubbo.config.*;
import com.alibaba.dubbo.rpc.service.GenericService;
import com.wl.common.scheduler.api.model.TaskVo;
import com.wl.service.scheduler.quartz.util.ApplicationBeanUtil;
import com.wl.service.scheduler.service.TaskService;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Administrator on 2020/7/16.
 */
public class DubboCommonJob implements Job{

    private Logger logger = LoggerFactory.getLogger(DubboCommonJob.class);

    private TaskService taskService;

    private static ApplicationConfig applicationConfig;

    private static RegistryConfig registryConfig;

    private static ThreadLocal<ReferenceConfig<GenericService>> referenceConfigThreadLocal = new ThreadLocal<>();

    private static ConcurrentHashMap<String, GenericService> serviceConcurrentHashMap = new ConcurrentHashMap<>();

    static {
        applicationConfig = ApplicationBeanUtil.getBean(ApplicationConfig.class);
        registryConfig = ApplicationBeanUtil.getBean(RegistryConfig.class);
    }

    public DubboCommonJob(){
        this.taskService = ApplicationBeanUtil.getBean(TaskService.class);
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobKey jobKey = context.getJobDetail().getKey();
        String jobName = jobKey.getName(), jobGroup = jobKey.getGroup();
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        String serviceInterface = jobDataMap.getString("serviceInterface");
        String serviceMethod = jobDataMap.getString("serviceMethod");
        String serviceGroup = jobDataMap.getString("serviceGroup");
        String serviceVersion = jobDataMap.getString("serviceVersion");
        String serviceMethodParameter = jobDataMap.getString("serviceMethodParameter");
        Boolean methodAsync = jobDataMap.getBoolean("serviceMethodAsync");
        Boolean methodReturn = jobDataMap.getBoolean("serviceMethodReturn");



        logger.info("\nserviceInterface:{}" +
                        " \nserviceMethod:{} " +
                        "\n serviceGroup:{}" +
                        " \n serviceVersion:{} " +
                        "\nserviceMethodParameter:{}" +
                        " \nserviceMethodAsync:{}" +
                        "\n serviceMethodReturn:{}\n",serviceInterface,serviceMethod,serviceGroup,
                serviceVersion,serviceMethodParameter,methodAsync,methodReturn);

        if(StringUtils.isBlank(serviceInterface)){
            logger.info("serviceInterface is null");
            return;
        }
        if(StringUtils.isBlank(serviceMethod)){
            logger.info("serviceMethod is null");
            return;
        }
        if(StringUtils.isBlank(serviceGroup)){
            logger.info("serviceGroup is null");
            return;
        }
//        if(StringUtils.isBlank(serviceMethodParameter)){
//            logger.info("serviceMethodParameter is null");
//            return;
//        }
        run(serviceInterface,
                serviceMethod,
                serviceGroup,
                serviceMethodParameter,
                serviceVersion,
                methodAsync,
                methodReturn);
        //更改任务执行状态为正在执行
        if(taskService != null) {
            taskService.updateTaskStatus(jobName, jobGroup, TaskVo.StateType.RUNNING.VALUE);
        }
    }

    private void run(String serviceInterface,
                     String serviceMethod,
                     String serviceGroup,
                     String serviceMethodParameter,
                     String serviceVersion,
                     Boolean methodAsync,
                     Boolean methodReturn) {
        if(applicationConfig == null || registryConfig == null){
            return;
        }
        GenericService service = genGenericService(serviceInterface,serviceMethod,serviceGroup,
                serviceMethodParameter,serviceVersion,methodAsync,methodReturn);
        Object obj = service.$invoke(serviceMethod,new String[]{"java.lang.String"},new String[]{serviceMethodParameter});
        //如果是异步执行,则返回值永远为null
        if(obj != null)
            logger.info(obj.toString());

    }

    private static GenericService genGenericService(String serviceInterface,
                                                    String serviceMethod,
                                                    String serviceGroup,
                                                    String serviceMethodParameter,
                                                    String serviceVersion,
                                                    Boolean methodAsync,
                                                    Boolean methodReturn) {
        String key = getKey(serviceInterface,serviceGroup,serviceMethod, serviceVersion,serviceMethodParameter);

        GenericService service = serviceConcurrentHashMap.get(key);
        if(service == null){
            ReferenceConfig<GenericService> config = referenceConfigThreadLocal.get();
            if(config == null){
                config = new ReferenceConfig<>();
                config.setApplication(applicationConfig);
                config.setRegistry(registryConfig);
                config.setInterface(serviceInterface);// 弱类型接口名 com.xxx.XxxService
                config.setVersion(serviceVersion);
                config.setGeneric(true); // 声明为泛化接口
                config.setGroup(serviceGroup);
                config.setAsync(methodAsync);

                ConsumerConfig consumerConfig = new ConsumerConfig();
                consumerConfig.setAsync(methodAsync);
                consumerConfig.setCheck(false);
                consumerConfig.setRetries(0);
                consumerConfig.setTimeout(6000000);
                config.setConsumer(consumerConfig);

                List<MethodConfig> methodConfigList = new ArrayList<MethodConfig>();
                MethodConfig methodConfig = new MethodConfig();
                methodConfig.setName(serviceMethod);
                methodConfig.setReturn(methodReturn);
                methodConfig.setAsync(methodAsync);
                methodConfig.setRetries(0);
                methodConfig.setTimeout(6000000);
                config.setMethods(methodConfigList);
                referenceConfigThreadLocal.set(config);
            }
            service = config.get();
            serviceConcurrentHashMap.put(key,service);
        }
        return service;
    }

    private static String getKey(String serviceInterface,
                                 String serviceGroup,
                                 String serviceMethod,
                                 String serviceVersion,
                                 String serviceMethodParameter) {

        return serviceInterface + "@" + serviceGroup + "@" + serviceMethod + "@" + serviceVersion + "@" + serviceMethodParameter;
    }

}

7.启动 zookeeper以及dubbo-admin(非必须)
8.测试代码

@Test
    public void testDubboJob() throws Exception {
        TaskVo taskVo = new TaskVo();
        taskVo.setId(3);
//        taskVo.setEndTime("2018-06-30 17:59:00");
        //初始化执行次数
        taskVo.setExecutedCount(0);
        taskVo.setExceptionCount(0);
        taskVo.setCronTriggerExpression("0/10 * * * * ? ");
        taskVo.setCronTriggerText("每十秒运行一次");
        taskVo.setDescription("通用http定时任务");
        taskVo.setStateType(TaskInfoFlowVo.StateType.NOT_EXECUTE.VALUE);

        taskVo.setJobClass("com.wl.service.scheduler.quartz.job.DubboCommonJob");
        taskVo.setJobGroup("dubboJob");
        taskVo.setJobName("通用dubboJob1");
        taskVo.setName("通用dubboJob1");
        taskVo.setCreateTime("2020-07-16 17:59:00");

        DubboDetail dubboDetail = new DubboDetail();
        dubboDetail.setServiceGroup("dubboJob");
        dubboDetail.setServiceInterface("com.wl.service.scheduler.dubbo.IDubboJobInterface");
        dubboDetail.setServiceMethod("dubboJob");
        dubboDetail.setServiceMethodAsync("false");
        dubboDetail.setServiceMethodReturn("true");
        dubboDetail.setServiceVersion("1.0.0");
        dubboDetail.setServiceMethodParameter(null);

        dubboDetail.setServiceMethodParameter("abcd");

        taskVo.setDubboDetail(new ObjectMapper().writeValueAsString(dubboDetail));

        schedulerService.deleteScheduler(taskVo);

        schedulerService.addScheduler(taskVo);

        schedulerService.startJob(taskVo);

        Thread.sleep(10000000L);

    }

执行日志

2020-07-16 17:50:18,859 INFO (StartupInfoLogger.java:57)- Started TestApplication in 13.442 seconds (JVM running for 13.994)
Hibernate: update task set state_type=? where job_name=? and job_group=?
Hibernate: select task0_.id as id1_0_0_, task0_.create_time as create_t2_0_0_, task0_.cron_trigger_expression as cron_tri3_0_0_, task0_.cron_trigger_text as cron_tri4_0_0_, task0_.description as descript5_0_0_, task0_.dubbo_detail as dubbo_de6_0_0_, task0_.end_time as end_time7_0_0_, task0_.exception_count as exceptio8_0_0_, task0_.executed_count as executed9_0_0_, task0_.http_detail as http_de10_0_0_, task0_.job_class as job_cla11_0_0_, task0_.job_group as job_gro12_0_0_, task0_.job_name as job_nam13_0_0_, task0_.name as name14_0_0_, task0_.next_fire_time as next_fi15_0_0_, task0_.prev_fire_time as prev_fi16_0_0_, task0_.start_time as start_t17_0_0_, task0_.state_type as state_t18_0_0_, task0_.this_fire_time as this_fi19_0_0_ from task task0_ where task0_.id=?
2020-07-16 17:50:19,048 INFO (SchedulerUtil.java:75)- add job to schedule jobName:通用dubboJob1 jobGroup:dubboJob
Hibernate: update task set create_time=?, cron_trigger_expression=?, cron_trigger_text=?, description=?, dubbo_detail=?, end_time=?, exception_count=?, executed_count=?, http_detail=?, job_class=?, job_group=?, job_name=?, name=?, next_fire_time=?, prev_fire_time=?, start_time=?, state_type=?, this_fire_time=? where id=?
2020-07-16 17:50:19,074 INFO (SchedulerUtil.java:88)- execute job jobName:通用dubboJob1 jobGroup:dubboJob
2020-07-16 17:50:20,035 INFO (QuartzJobListener.java:43)- 任务 job:通用dubboJob1 group:dubboJob,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 17:50:20,046 INFO (DubboCommonJob.java:58)- 
serviceInterface:com.wl.service.scheduler.dubbo.IDubboJobInterface 
serviceMethod:dubboJob 
 serviceGroup:dubboJob 
 serviceVersion:1.0.0 
serviceMethodParameter:abcd 
serviceMethodAsync:false
 serviceMethodReturn:true

2020-07-16 17:50:20,051 INFO (Log4jLogger.java:59)-  [DUBBO] Register: consumer://192.168.92.1/com.alibaba.dubbo.rpc.service.GenericService?application=scheduler-service&async=false&category=consumers&check=false&default.async=false&default.check=false&default.retries=0&default.timeout=6000000&dubbo=2.6.0&generic=true&group=dubboJob&interface=com.wl.service.scheduler.dubbo.IDubboJobInterface&pid=11564&side=consumer&timestamp=1594893020049&version=1.0.0, dubbo version: 2.6.0, current host: 192.168.92.1
2020-07-16 17:50:20,054 INFO (Log4jLogger.java:59)-  [DUBBO] Subscribe: consumer://192.168.92.1/com.alibaba.dubbo.rpc.service.GenericService?application=scheduler-service&async=false&category=providers,configurators,routers&default.async=false&default.check=false&default.retries=0&default.timeout=6000000&dubbo=2.6.0&generic=true&group=dubboJob&interface=com.wl.service.scheduler.dubbo.IDubboJobInterface&pid=11564&side=consumer&timestamp=1594893020049&version=1.0.0, dubbo version: 2.6.0, current host: 192.168.92.1
2020-07-16 17:50:20,064 INFO (Log4jLogger.java:59)-  [DUBBO] Notify urls for subscribe url consumer://192.168.92.1/com.alibaba.dubbo.rpc.service.GenericService?application=scheduler-service&async=false&category=providers,configurators,routers&default.async=false&default.check=false&default.retries=0&default.timeout=6000000&dubbo=2.6.0&generic=true&group=dubboJob&interface=com.wl.service.scheduler.dubbo.IDubboJobInterface&pid=11564&side=consumer&timestamp=1594893020049&version=1.0.0, urls: [dubbo://192.168.92.1:20212/com.wl.service.scheduler.dubbo.IDubboJobInterface?anyhost=true&application=scheduler-service&default.retries=0&default.version=1.0.0&dubbo=2.6.0&generic=false&group=dubboJob&interface=com.wl.service.scheduler.dubbo.IDubboJobInterface&methods=dubboJob&pid=11564&revision=1.0.0&side=provider&timestamp=1594893009389&version=1.0.0, empty://192.168.92.1/com.alibaba.dubbo.rpc.service.GenericService?application=scheduler-service&async=false&category=configurators&default.async=false&default.check=false&default.retries=0&default.timeout=6000000&dubbo=2.6.0&generic=true&group=dubboJob&interface=com.wl.service.scheduler.dubbo.IDubboJobInterface&pid=11564&side=consumer&timestamp=1594893020049&version=1.0.0, empty://192.168.92.1/com.alibaba.dubbo.rpc.service.GenericService?application=scheduler-service&async=false&category=routers&default.async=false&default.check=false&default.retries=0&default.timeout=6000000&dubbo=2.6.0&generic=true&group=dubboJob&interface=com.wl.service.scheduler.dubbo.IDubboJobInterface&pid=11564&side=consumer&timestamp=1594893020049&version=1.0.0], dubbo version: 2.6.0, current host: 192.168.92.1
2020-07-16 17:50:20,092 INFO (Log4jLogger.java:59)-  [DUBBO] Successed connect to server /192.168.92.1:20212 from NettyClient 192.168.92.1 using dubbo version 2.6.0, channel is NettyChannel [channel=[id: 0x7b41b504, /192.168.92.1:52528 => /192.168.92.1:20212]], dubbo version: 2.6.0, current host: 192.168.92.1
2020-07-16 17:50:20,092 INFO (Log4jLogger.java:59)-  [DUBBO] Start NettyClient ILML1N09A7L7UC3/192.168.92.1 connect to the server /192.168.92.1:20212, dubbo version: 2.6.0, current host: 192.168.92.1
2020-07-16 17:50:20,099 INFO (Log4jLogger.java:59)-  [DUBBO] Refer dubbo service com.alibaba.dubbo.rpc.service.GenericService from url zookeeper://192.168.92.128:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=scheduler-service&async=false&check=false&default.async=false&default.check=false&default.retries=0&default.timeout=6000000&default.version=1.0.0&dubbo=2.6.0&generic=true&group=dubboJob&interface=com.wl.service.scheduler.dubbo.IDubboJobInterface&methods=dubboJob&pid=11564&register.ip=192.168.92.1&remote.timestamp=1594893009389&revision=1.0.0&side=consumer&timestamp=1594893020049&version=1.0.0, dubbo version: 2.6.0, current host: 192.168.92.1
======================================abcd
2020-07-16 17:50:20,155 INFO (Log4jLogger.java:59)-  [DUBBO] Executor for listenablefuture is null, will use default executor!, dubbo version: 2.6.0, current host: 192.168.92.1
2020-07-16 17:50:20,156 INFO (Log4jLogger.java:59)-  [DUBBO] Register: consumer:///com.alibaba.dubbo.monitor.MonitorService?category=consumers&check=false&dubbo=2.6.0&interface=com.alibaba.dubbo.monitor.MonitorService&pid=11564&timestamp=1594893009448, dubbo version: 2.6.0, current host: 192.168.92.1
2020-07-16 17:50:20,157 INFO (DubboCommonJob.java:110)- abcd
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 17:50:20,161 INFO (Log4jLogger.java:59)-  [DUBBO] Subscribe: consumer:///com.alibaba.dubbo.monitor.MonitorService?category=providers,configurators,routers&dubbo=2.6.0&interface=com.alibaba.dubbo.monitor.MonitorService&pid=11564&timestamp=1594893009448, dubbo version: 2.6.0, current host: 192.168.92.1
2020-07-16 17:50:20,161 INFO (QuartzJobListener.java:87)- 任务 job:通用dubboJob1 group:dubboJob 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
2020-07-16 17:50:20,168 INFO (Log4jLogger.java:59)-  [DUBBO] Notify urls for subscribe url consumer:///com.alibaba.dubbo.monitor.MonitorService?category=providers,configurators,routers&dubbo=2.6.0&interface=com.alibaba.dubbo.monitor.MonitorService&pid=11564&timestamp=1594893009448, urls: [empty:///com.alibaba.dubbo.monitor.MonitorService?category=providers&dubbo=2.6.0&interface=com.alibaba.dubbo.monitor.MonitorService&pid=11564&timestamp=1594893009448, empty:///com.alibaba.dubbo.monitor.MonitorService?category=configurators&dubbo=2.6.0&interface=com.alibaba.dubbo.monitor.MonitorService&pid=11564&timestamp=1594893009448, empty:///com.alibaba.dubbo.monitor.MonitorService?category=routers&dubbo=2.6.0&interface=com.alibaba.dubbo.monitor.MonitorService&pid=11564&timestamp=1594893009448], dubbo version: 2.6.0, current host: 192.168.92.1
2020-07-16 17:50:30,018 INFO (QuartzJobListener.java:43)- 任务 job:通用dubboJob1 group:dubboJob,即将执行

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=? where job_name=? and job_group=?
2020-07-16 17:50:30,022 INFO (DubboCommonJob.java:58)- 
serviceInterface:com.wl.service.scheduler.dubbo.IDubboJobInterface 
serviceMethod:dubboJob 
 serviceGroup:dubboJob 
 serviceVersion:1.0.0 
serviceMethodParameter:abcd 
serviceMethodAsync:false
 serviceMethodReturn:true

======================================abcd
2020-07-16 17:50:30,024 INFO (DubboCommonJob.java:110)- abcd
Hibernate: update task set state_type=? where job_name=? and job_group=?
2020-07-16 17:50:30,028 INFO (QuartzJobListener.java:87)- 任务 job:通用dubboJob1 group:dubboJob 执行完毕

Hibernate: update task set prev_fire_time=?, this_fire_time=?, next_fire_time=?, state_type=?, executed_count=executed_count+1 where job_name=? and job_group=?
Disconnected from the target VM, address: '127.0.0.1:52506', transport: 'socket'

可以看到泛化调用IDubboJobInterface 接口实现方法成功打印======================================abcd
dubbo-admin截图
在这里插入图片描述

使用spring-cloud调用外部服务

与http类似,只需要引入spring-cloud的依赖包稍加改造即可

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值