spring-boot-2.0.3之quartz集成,不是你想的那样哦!

开心一刻

  晚上回家,爸妈正在吵架,见我回来就都不说话了

  看见我妈坐在那里瞪着我爸,我就问老爸:“你干什么了惹我妈生这么大气?” 

  我爸说:“没有什么啊,倒是你,这么大了还没有媳妇,要是你有媳妇给我们生一个孙子玩,我们至于吵架吗?”

  我一听就感觉要坏,老爸你这是来了一招调虎离山啊,实力坑儿子啊

  果然我妈改瞪我了,然后完全不理我爸,直接指着我开骂了……

java 定时任务调度的实现方式 

  Timer

    这个相信大家都有用过,我也用过,但用的不多;

    特点是:简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务;能实现简单的定时任务,稍微复杂点(或要求高一些)的定时任务却不好实现。
  ScheduledExecutor

    这个我相信大家也都用过,而且用的比 Timer 多;正是鉴于 Timer 的缺陷,Java 5 推出了基于线程池设计的 ScheduledExecutor;

    特点:每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。需要注意的是,只有当任务的执行时间到来时,ScheduedExecutor 才会真正启动一个线程,其余时间 ScheduledExecutor 都是在轮询任务的状态。

    虽然用 ScheduledExecutor 和 Calendar 能够实现复杂任务调度,但实现起来还是比较麻烦,对开发还是不够友善。

  Spring Scheduler

    spring 对任务调度的实现支持,可以指定任务的执行时间,但对任务队列和线程池的管控较弱;一般集成于项目中,小任务很方便。
  JCronTab

    JCronTab 则是一款完全按照 crontab 语法编写的 java 任务调度工具。

    特点:

      可指定任务的执行时间;

      提供完全按照 Unix 的 UNIX-POSIX crontab 的格式来规定时间;

      支持多种任务调度的持久化方法,包括普通文件、数据库以及 XML 文件进行持久化;

      JCronTab 内置了发邮件功能,可以将任务执行结果方便地发送给需要被通知的人;

      设计和部署是高性能并可扩展。

  Quartz

    本文主角,请往下看
  当然还有 XXL-JOB、Elastic-Job、Saturn、dolphinscheduler 等等

quartz 相关概念

  Scheduler:调度器,进行任务调度;quartz 的大脑
  Job:业务 job,亦可称业务组件;定时任务的具体执行业务需要实现此接口,调度器会调用此接口的 execute 方法完成我们的定时业务
  JobDetail:用来定义业务 Job 的实例,我们可以称之为 quartz job,很多时候我们谈到的 job 指的是 JobDetail
  Trigger:触发器,用来定义一个指定的 Job 何时被执行
  JobBuilder:Job 构建器,用来定义或创建 JobDetail 的实例;JobDetail 限定了只能是 Job 的实例
  TriggerBuilder:触发器构建器,用来定义或创建触发器的实例

  具体为什么要分这么细,大家可以去查阅下相关资料,你会发现很多东西

工程实现

  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>
&lt;groupId&gt;com.lee&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-quartz&lt;/artifactId&gt;
&lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;

&lt;properties&gt;
    &lt;java.version&gt;1.8&lt;/java.version&gt;
    &lt;maven.compiler.source&gt;1.8&lt;/maven.compiler.source&gt;
    &lt;maven.compiler.target&gt;1.8&lt;/maven.compiler.target&gt;
    &lt;druid.version&gt;1.1.10&lt;/druid.version&gt;
    &lt;pagehelper.version&gt;1.2.5&lt;/pagehelper.version&gt;
    &lt;druid.version&gt;1.1.10&lt;/druid.version&gt;
&lt;/properties&gt;

&lt;parent&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
    &lt;version&gt;2.0.3.RELEASE&lt;/version&gt;
&lt;/parent&gt;

&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;/artifactId&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-quartz&lt;/artifactId&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;com.alibaba&lt;/groupId&gt;
        &lt;artifactId&gt;druid-spring-boot-starter&lt;/artifactId&gt;
        &lt;version&gt;${druid.version}&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;mysql&lt;/groupId&gt;
        &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.github.pagehelper&lt;/groupId&gt;
        &lt;artifactId&gt;pagehelper-spring-boot-starter&lt;/artifactId&gt;
        &lt;version&gt;${pagehelper.version}&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;!-- 日志 --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-logging&lt;/artifactId&gt;
        &lt;exclusions&gt;            &lt;!-- 排除spring-boot-starter-logging中的全部依赖 --&gt;
            &lt;exclusion&gt;
                &lt;groupId&gt;*&lt;/groupId&gt;
                &lt;artifactId&gt;*&lt;/artifactId&gt;
            &lt;/exclusion&gt;
        &lt;/exclusions&gt;
        &lt;scope&gt;test&lt;/scope&gt;     &lt;!-- 打包的时候不打spring-boot-starter-logging.jar --&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
        &lt;artifactId&gt;logback-classic&lt;/artifactId&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
        &lt;artifactId&gt;lombok&lt;/artifactId&gt;
        &lt;optional&gt;<span style="color: rgba(0, 0, 255, 1)">true</span>&lt;/optional&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;build&gt;
    &lt;finalName&gt;spring-boot-quartz&lt;/finalName&gt;
    &lt;plugins&gt;
        &lt;!-- 打包项目 mvn clean <span style="color: rgba(0, 0, 255, 1)">package</span> --&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
        &lt;/plugin&gt;
    &lt;/plugins&gt;
&lt;/build&gt;

</project>

View Code

  application.xml

server:
  port: 9001
  servlet:
    context-path: /quartz
spring:
  thymeleaf:
    mode: HTML
    cache: false
  #连接池配置
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/spring-boot-quartz?useSSL=false&useUnicode=true
      username: root
      password: 123456
      initial-size: 1                     #连接池初始大小
      max-active: 20                      #连接池中最大的活跃连接数
      min-idle: 1                         #连接池中最小的活跃连接数
      max-wait: 60000                     #配置获取连接等待超时的时间
      pool-prepared-statements: true    #打开 PSCache,并且指定每个连接上 PSCache 的大小
      max-pool-prepared-statement-per-connection-size: 20
      validation-query: SELECT 1 FROM DUAL
      validation-query-timeout: 30000
      test-on-borrow: false             #是否在获得连接后检测其可用性
      test-on-return: false             #是否在连接放回连接池后检测其可用性
      test-while-idle: true             #是否在连接空闲一段时间后检测其可用性
  quartz:
    #相关属性配置
    properties:
      org:
        quartz:
          scheduler:
            instanceName: quartzScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_
            isClustered: false
            clusterCheckinInterval: 10000
            useProperties: false
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
    #数据库方式
    job-store-type: JDBC
    #初始化表结构
    jdbc:
      initialize-schema: NEVER
#mybatis 配置
mybatis:
  type-aliases-package: com.lee.quartz.entity
  mapper-locations: classpath:mybatis/mapper/*.xml
#分页配置, pageHelper 是物理分页插件
pagehelper:
  #4.0.0 以后版本可以不设置该参数,该示例中是 5.1.4
  helper-dialect: mysql
  #启用合理化,如果 pageNum<1 会查询第一页,如果 pageNum>pages 会查询最后一页
  reasonable: true
logging:
  level:
    com.lee.quartz.mapper: debug
View Code

  这样,quartz 就配置好了,应用里面直接用即可

  JobController.java

package com.lee.quartz.web;

import com.github.pagehelper.PageInfo;
import com.lee.quartz.common.Result;
import com.lee.quartz.entity.QuartzJob;
import com.lee.quartz.service.IJobService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/job")
public class JobController {
private final static Logger LOGGER = LoggerFactory.getLogger(JobController.class);

@Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> IJobService jobService;

@SuppressWarnings({ </span>"unchecked", "rawtypes"<span style="color: rgba(0, 0, 0, 1)"> })
@PostMapping(</span>"/add"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result save(QuartzJob quartz){
    LOGGER.info(</span>"新增任务"<span style="color: rgba(0, 0, 0, 1)">);
    Result result </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.saveJob(quartz);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
}
@PostMapping(</span>"/list"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> PageInfo list(String jobName,Integer pageNo,Integer pageSize){
    LOGGER.info(</span>"任务列表"<span style="color: rgba(0, 0, 0, 1)">);
    PageInfo pageInfo </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.listQuartzJob(jobName, pageNo, pageSize);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pageInfo;
}

@PostMapping(</span>"/trigger"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)">  Result trigger(String jobName, String jobGroup) {
    LOGGER.info(</span>"触发任务"<span style="color: rgba(0, 0, 0, 1)">);
    Result result </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.triggerJob(jobName, jobGroup);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
}

@PostMapping(</span>"/pause"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)">  Result pause(String jobName, String jobGroup) {
    LOGGER.info(</span>"停止任务"<span style="color: rgba(0, 0, 0, 1)">);
    Result result </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.pauseJob(jobName, jobGroup);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
}

@PostMapping(</span>"/resume"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)">  Result resume(String jobName, String jobGroup) {
    LOGGER.info(</span>"恢复任务"<span style="color: rgba(0, 0, 0, 1)">);
    Result result </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.resumeJob(jobName, jobGroup);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
}

@PostMapping(</span>"/remove"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)">  Result remove(String jobName, String jobGroup) {
    LOGGER.info(</span>"移除任务"<span style="color: rgba(0, 0, 0, 1)">);
    Result result </span>=<span style="color: rgba(0, 0, 0, 1)"> jobService.removeJob(jobName, jobGroup);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
}

}

View Code

  JobServiceImpl.java

package com.lee.quartz.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lee.quartz.common.Result;
import com.lee.quartz.entity.QuartzJob;
import com.lee.quartz.mapper.JobMapper;
import com.lee.quartz.service.IJobService;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class JobServiceImpl implements IJobService {

@Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> Scheduler scheduler;

@Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> JobMapper jobMapper;

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> PageInfo listQuartzJob(String jobName, Integer pageNum, Integer pageSize) {
    PageHelper.startPage(pageNum, pageSize);
    List</span>&lt;QuartzJob&gt; jobList =<span style="color: rgba(0, 0, 0, 1)"> jobMapper.listJob(jobName);
    PageInfo pageInfo </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PageInfo(jobList);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pageInfo;
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result saveJob(QuartzJob quartz){
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果是修改  展示旧的 任务</span>
        <span style="color: rgba(0, 0, 255, 1)">if</span>(quartz.getOldJobGroup() != <span style="color: rgba(0, 0, 255, 1)">null</span> &amp;&amp; !""<span style="color: rgba(0, 0, 0, 1)">.equals(quartz.getOldJobGroup())){
            JobKey key </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JobKey(quartz.getOldJobName(),quartz.getOldJobGroup());
            scheduler.deleteJob(key);
        }

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">构建job信息</span>
        Class cls =<span style="color: rgba(0, 0, 0, 1)"> Class.forName(quartz.getJobClassName()) ;
        cls.newInstance();
        JobDetail job </span>=<span style="color: rgba(0, 0, 0, 1)"> JobBuilder.newJob(cls).withIdentity(quartz.getJobName(),
                quartz.getJobGroup())
                .withDescription(quartz.getDescription()).build();
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 触发时间点</span>
        CronScheduleBuilder cronScheduleBuilder =<span style="color: rgba(0, 0, 0, 1)"> CronScheduleBuilder.cronSchedule(quartz.getCronExpression().trim());
        Trigger trigger </span>= TriggerBuilder.newTrigger().withIdentity("trigger"+<span style="color: rgba(0, 0, 0, 1)">quartz.getJobName(), quartz.getJobGroup())
                .startNow().withSchedule(cronScheduleBuilder).build();
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">交由Scheduler安排触发</span>

scheduler.scheduleJob(job, trigger);
}
catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result triggerJob(String jobName, String jobGroup) {
    JobKey key </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JobKey(jobName,jobGroup);
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        scheduler.triggerJob(key);
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SchedulerException e) {
        e.printStackTrace();
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.error();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.ok();
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result pauseJob(String jobName, String jobGroup) {
    JobKey key </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JobKey(jobName,jobGroup);
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        scheduler.pauseJob(key);
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SchedulerException e) {
        e.printStackTrace();
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.error();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.ok();
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result resumeJob(String jobName, String jobGroup) {
    JobKey key </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JobKey(jobName,jobGroup);
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        scheduler.resumeJob(key);
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SchedulerException e) {
        e.printStackTrace();
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.error();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Result.ok();
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Result removeJob(String jobName, String jobGroup) {
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        TriggerKey triggerKey </span>=<span style="color: rgba(0, 0, 0, 1)"> TriggerKey.triggerKey(jobName, jobGroup);
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 停止触发器</span>

scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
System.out.println(
"removeJob:"+JobKey.jobKey(jobName));
}
catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}
}

View Code

  主要就是以上文件,详情请查看spring-boot-quartz

  工程里面数据源用的 druid,springboot 默认也会将该数据源应用到 quartz,如果想给 quartz 单独配置数据源,可配合 @QuartzDataSource 来实现(更多 quarz 数据源问题,请查看spring-boot-2.0.3 之 quartz 集成,数据源问题,源码探究

  最终效果如下

trigger 状态

  org.quartz.impl.jdbcjobstore.Constants 中存放了一些列的常量,源代码如下

/* 
 * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
 * use this file except in compliance with the License. You may obtain a copy 
 * of the License at 
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0 
 *   
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
 * License for the specific language governing permissions and limitations 
 * under the License.
 * 
 */

package org.quartz.impl.jdbcjobstore;

/**

  • <p>

  • This interface can be implemented by any <code>{@link

  • org.quartz.impl.jdbcjobstore.DriverDelegate}</code>

  • class that needs to use the constants contained herein.

  • </p>

  • @author <a href="mailto:jeff@binaryfeed.org">Jeffrey Wescott</a>

  • @author James House
    */
    public interface Constants {

    /*

    • Constants.

    */

    // Table names
    String TABLE_JOB_DETAILS = "JOB_DETAILS";

    String TABLE_TRIGGERS = "TRIGGERS";

    String TABLE_SIMPLE_TRIGGERS = "SIMPLE_TRIGGERS";

    String TABLE_CRON_TRIGGERS = "CRON_TRIGGERS";

    String TABLE_BLOB_TRIGGERS = "BLOB_TRIGGERS";

    String TABLE_FIRED_TRIGGERS = "FIRED_TRIGGERS";

    String TABLE_CALENDARS = "CALENDARS";

    String TABLE_PAUSED_TRIGGERS = "PAUSED_TRIGGER_GRPS";

    String TABLE_LOCKS = "LOCKS";

    String TABLE_SCHEDULER_STATE = "SCHEDULER_STATE";

    // TABLE_JOB_DETAILS columns names

String COL_SCHEDULER_NAME = "SCHED_NAME";
String COL_JOB_NAME </span>= "JOB_NAME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_JOB_GROUP </span>= "JOB_GROUP"<span style="color: rgba(0, 0, 0, 1)">;

String COL_IS_DURABLE </span>= "IS_DURABLE"<span style="color: rgba(0, 0, 0, 1)">;

String COL_IS_VOLATILE </span>= "IS_VOLATILE"<span style="color: rgba(0, 0, 0, 1)">;

String COL_IS_NONCONCURRENT </span>= "IS_NONCONCURRENT"<span style="color: rgba(0, 0, 0, 1)">;

String COL_IS_UPDATE_DATA </span>= "IS_UPDATE_DATA"<span style="color: rgba(0, 0, 0, 1)">;

String COL_REQUESTS_RECOVERY </span>= "REQUESTS_RECOVERY"<span style="color: rgba(0, 0, 0, 1)">;

String COL_JOB_DATAMAP </span>= "JOB_DATA"<span style="color: rgba(0, 0, 0, 1)">;

String COL_JOB_CLASS </span>= "JOB_CLASS_NAME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_DESCRIPTION </span>= "DESCRIPTION"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_TRIGGERS columns names</span>
String COL_TRIGGER_NAME = "TRIGGER_NAME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_TRIGGER_GROUP </span>= "TRIGGER_GROUP"<span style="color: rgba(0, 0, 0, 1)">;

String COL_NEXT_FIRE_TIME </span>= "NEXT_FIRE_TIME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_PREV_FIRE_TIME </span>= "PREV_FIRE_TIME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_TRIGGER_STATE </span>= "TRIGGER_STATE"<span style="color: rgba(0, 0, 0, 1)">;

String COL_TRIGGER_TYPE </span>= "TRIGGER_TYPE"<span style="color: rgba(0, 0, 0, 1)">;

String COL_START_TIME </span>= "START_TIME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_END_TIME </span>= "END_TIME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_PRIORITY </span>= "PRIORITY"<span style="color: rgba(0, 0, 0, 1)">;

String COL_MISFIRE_INSTRUCTION </span>= "MISFIRE_INSTR"<span style="color: rgba(0, 0, 0, 1)">;

String ALIAS_COL_NEXT_FIRE_TIME </span>= "ALIAS_NXT_FR_TM"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_SIMPLE_TRIGGERS columns names</span>
String COL_REPEAT_COUNT = "REPEAT_COUNT"<span style="color: rgba(0, 0, 0, 1)">;

String COL_REPEAT_INTERVAL </span>= "REPEAT_INTERVAL"<span style="color: rgba(0, 0, 0, 1)">;

String COL_TIMES_TRIGGERED </span>= "TIMES_TRIGGERED"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_CRON_TRIGGERS columns names</span>
String COL_CRON_EXPRESSION = "CRON_EXPRESSION"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_BLOB_TRIGGERS columns names</span>
String COL_BLOB = "BLOB_DATA"<span style="color: rgba(0, 0, 0, 1)">;

String COL_TIME_ZONE_ID </span>= "TIME_ZONE_ID"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_FIRED_TRIGGERS columns names</span>
String COL_INSTANCE_NAME = "INSTANCE_NAME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_FIRED_TIME </span>= "FIRED_TIME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_SCHED_TIME </span>= "SCHED_TIME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_ENTRY_ID </span>= "ENTRY_ID"<span style="color: rgba(0, 0, 0, 1)">;

String COL_ENTRY_STATE </span>= "STATE"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_CALENDARS columns names</span>
String COL_CALENDAR_NAME = "CALENDAR_NAME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_CALENDAR </span>= "CALENDAR"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_LOCKS columns names</span>
String COL_LOCK_NAME = "LOCK_NAME"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TABLE_LOCKS columns names</span>
String COL_LAST_CHECKIN_TIME = "LAST_CHECKIN_TIME"<span style="color: rgba(0, 0, 0, 1)">;

String COL_CHECKIN_INTERVAL </span>= "CHECKIN_INTERVAL"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> MISC CONSTANTS</span>
String DEFAULT_TABLE_PREFIX = "QRTZ_"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> STATES</span>
String STATE_WAITING = "WAITING"<span style="color: rgba(0, 0, 0, 1)">;

String STATE_ACQUIRED </span>= "ACQUIRED"<span style="color: rgba(0, 0, 0, 1)">;

String STATE_EXECUTING </span>= "EXECUTING"<span style="color: rgba(0, 0, 0, 1)">;

String STATE_COMPLETE </span>= "COMPLETE"<span style="color: rgba(0, 0, 0, 1)">;

String STATE_BLOCKED </span>= "BLOCKED"<span style="color: rgba(0, 0, 0, 1)">;

String STATE_ERROR </span>= "ERROR"<span style="color: rgba(0, 0, 0, 1)">;

String STATE_PAUSED </span>= "PAUSED"<span style="color: rgba(0, 0, 0, 1)">;

String STATE_PAUSED_BLOCKED </span>= "PAUSED_BLOCKED"<span style="color: rgba(0, 0, 0, 1)">;

String STATE_DELETED </span>= "DELETED"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * </span><span style="color: rgba(128, 128, 128, 1)">@deprecated</span><span style="color: rgba(0, 128, 0, 1)"> Whether a trigger has misfired is no longer a state, but 
 * rather now identified dynamically by whether the trigger's next fire 
 * time is more than the misfire threshold time in the past.
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String STATE_MISFIRED </span>= "MISFIRED"<span style="color: rgba(0, 0, 0, 1)">;

String ALL_GROUPS_PAUSED </span>= "_$_ALL_GROUPS_PAUSED_$_"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TRIGGER TYPES</span>
<span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Simple Trigger type. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String TTYPE_SIMPLE </span>= "SIMPLE"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Cron Trigger type. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String TTYPE_CRON </span>= "CRON"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Calendar Interval Trigger type. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String TTYPE_CAL_INT </span>= "CAL_INT"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Daily Time Interval Trigger type. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String TTYPE_DAILY_TIME_INT </span>= "DAILY_I"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> A general blob Trigger type. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String TTYPE_BLOB </span>= "BLOB"<span style="color: rgba(0, 0, 0, 1)">;

}

// EOF

View Code

  里面有 quartz 的表名、各个表包含的列名、trigger 状态、trigger 类型等内容

  状态包括

    WAITING:等待中
    ACQUIRED:将触发,此时还未到 trigger 真正的触发时刻
    EXECUTING:触发,亦可理解成执行中,trigger 真正的触发时刻
    COMPLETE:完成,不再触发
    BLOCKED:受阻,不允许并发执行 job 时会出现(@DisallowConcurrentExecution)
    ERROR:出错
    PAUSED:暂停中
    PAUSED_BLOCKED:暂停受阻,不允许并发执行 job 时会出现(@DisallowConcurrentExecution)
    DELETED:已删除
    MISFIRED:触发失败,已弃用,有另外的替代方式

  状态变化流程图如下所示

  trigger 的初始状态是 WAITING,处于 WAITING 状态的 trigger 等待被触发。调度线程会不停地扫 triggers 表,根据 NEXT_FIRE_TIME 提前拉取即将触发的 trigger,如果这个 trigger 被该调度线程拉取到,它的状态就会变为 ACQUIRED。因为是提前拉取 trigger,并未到达 trigger 真正的触发时刻,所以调度线程会等到真正触发的时刻,再将 trigger 状态由 ACQUIRED 改为 EXECUTING。如果这个 trigger 不再执行,就将状态改为 COMPLETE, 否则为 WAITING,开始新的周期。如果这个周期中的任何环节抛出异常,trigger 的状态会变成 ERROR。如果手动暂停这个 trigger,状态会变成 PAUSED。

总结

  Quartz 作为一个开源的作业调度框架,提供了巨大的灵活性而不牺牲简单性。我们能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库、集群、插件、JavaMail 支持,EJB 作业预构建,支持 cron-like 表达式等等;

  springboot 集成 quartz 非常简单,最简单的情况下只需要引入依赖我们就可以享受 quartz 提供的功能,springboot 默认会帮我们配置好 quartz;当然我们也可以自定义配置来实现 quartz 的定制;

参考

  几种任务调度的 Java 实现方法与比较

  小柒 2012 / spring-boot-quartz

  boot-features-quartz

  作业调度系统—Quartz

  记一次 Quartz 重复调度 (任务重复执行) 的问题排查

  Quartz FAQ