最为常用定时任务框架是Quartz,并且Spring也集成了Quartz的框架,Quartz不仅支持单实例方式还支持分布式方式。本文主要介绍Quartz,基础的Quartz的集成案例本,以及实现基于数据库的分布式任务管理和控制job生命周期。@pdai

准备知识点

需要了解常用的Quartz框架。

什么是Quartz

来源百度百科, 官网地址:http://www.quartz-scheduler.org/

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。

它的特点如下

  • 纯java实现,可以作为独立的应用程序,也可以嵌入在另一个独立式应用程序运行
  • 强大的调度功能,Spring默认的调度框架,灵活可配置;
  • 作业持久化,调度环境持久化机制,可以保存并恢复调度现场。系统关闭数据不会丢失;灵活的应用方式,可以任意定义触发器的调度时间表,支持任务和调度各种组合,组件式监听器、各种插件、线程池等功能,多种存储方式等;
  • 分布式和集群能力,可以被实例化,一个Quartz集群中的每个节点作为一个独立的Quartz使用,通过相同的数据库表来感知到另一个Quartz应用

Quartz的体系结构

  • Job 表示一个工作,要执行的具体内容。
  • JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
  • Trigger 代表一个调度参数的配置,什么时候去调。
  • Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。

注: 上图来源于https://www.cnblogs.com/jijm123/p/14240320.html

什么是Quartz持久化

  • 为什么要持久化?

当程序突然被中断时,如断电,内存超出时,很有可能造成任务的丢失。 可以将调度信息存储到数据库里面,进行持久化,当程序被中断后,再次启动,仍然会保留中断之前的数据,继续执行,而并不是重新开始。

  • Quartz提供了两种持久化方式

Quartz提供两种基本作业存储类型:

  1. RAMJobStore

在默认情况下Quartz将任务调度的运行信息保存在内存中,这种方法提供了最佳的性能,因为内存中数据访问最快。不足之处是缺乏数据的持久性,当程序路途停止或系统崩溃时,所有运行的信息都会丢失。

  1. JobStoreTX

所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务。

实现案例 - 单实例方式

本例将展示quartz实现单实例方式。

  • 引入POM依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
  • 定义Job

只需要继承QuartzJobBean,并重载executeInternal方法即可定义你自己的Job执行逻辑。

@Slf4j
public class HelloJob extends QuartzJobBean { @Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// get parameters
context.getJobDetail().getJobDataMap().forEach(
(k, v) -> log.info("param, key:{}, value:{}", k, v)
);
// your logics
log.info("Hello Job执行时间: " + new Date());
}
}
  • 配置Job

JobDetail, Trigger, Schedule(这里采用CronScheduleBuilder)

/**
* @author pdai
*/
@Configuration
public class QuartzConfig { @Bean("helloJob")
public JobDetail helloJobDetail() {
return JobBuilder.newJob(HelloJob.class)
.withIdentity("DateTimeJob")
.usingJobData("msg", "Hello Quartz")
.storeDurably()//即使没有Trigger关联时,也不需要删除该JobDetail
.build();
} @Bean
public Trigger printTimeJobTrigger() {
// 每秒执行一次
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ?");
return TriggerBuilder.newTrigger()
.forJob(helloJobDetail())
.withIdentity("quartzTaskService")
.withSchedule(cronScheduleBuilder)
.build();
}
}
  • 执行测试
2021-10-01 13:09:00.380  INFO 38484 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-10-01 13:09:00.391 INFO 38484 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-10-01 13:09:00.392 INFO 38484 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.50]
2021-10-01 13:09:00.526 INFO 38484 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-10-01 13:09:00.526 INFO 38484 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1424 ms
2021-10-01 13:09:00.866 INFO 38484 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
2021-10-01 13:09:00.877 INFO 38484 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2021-10-01 13:09:00.877 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
2021-10-01 13:09:00.878 INFO 38484 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
2021-10-01 13:09:00.878 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'quartzScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered. 2021-10-01 13:09:00.878 INFO 38484 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance.
2021-10-01 13:09:00.879 INFO 38484 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
2021-10-01 13:09:00.879 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@6075b2d3
2021-10-01 13:09:00.922 INFO 38484 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-10-01 13:09:00.923 INFO 38484 --- [ main] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now
2021-10-01 13:09:00.923 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started.
2021-10-01 13:09:00.933 INFO 38484 --- [ main] tech.pdai.springboot.quartz.App : Started App in 2.64 seconds (JVM running for 3.621)
2021-10-01 13:09:00.931 INFO 38484 --- [eduler_Worker-1] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:00.933 INFO 38484 --- [eduler_Worker-1] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:00 CST 2021
2021-10-01 13:09:01.001 INFO 38484 --- [eduler_Worker-2] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:01.001 INFO 38484 --- [eduler_Worker-2] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:01 CST 2021
2021-10-01 13:09:02.000 INFO 38484 --- [eduler_Worker-3] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:02.000 INFO 38484 --- [eduler_Worker-3] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:02 CST 2021
2021-10-01 13:09:03.000 INFO 38484 --- [eduler_Worker-4] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:03.001 INFO 38484 --- [eduler_Worker-4] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:03 CST 2021
2021-10-01 13:09:04.001 INFO 38484 --- [eduler_Worker-5] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:04.001 INFO 38484 --- [eduler_Worker-5] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:04 CST 2021
2021-10-01 13:09:05.002 INFO 38484 --- [eduler_Worker-6] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:05.003 INFO 38484 --- [eduler_Worker-6] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:05 CST 2021
2021-10-01 13:09:06.000 INFO 38484 --- [eduler_Worker-7] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:06.001 INFO 38484 --- [eduler_Worker-7] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:06 CST 2021
2021-10-01 13:09:07.002 INFO 38484 --- [eduler_Worker-8] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:07.002 INFO 38484 --- [eduler_Worker-8] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:07 CST 2021
2021-10-01 13:09:08.002 INFO 38484 --- [eduler_Worker-9] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:08.003 INFO 38484 --- [eduler_Worker-9] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:08 CST 2021
2021-10-01 13:09:09.000 INFO 38484 --- [duler_Worker-10] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:09.000 INFO 38484 --- [duler_Worker-10] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:09 CST 2021
2021-10-01 13:09:10.001 INFO 38484 --- [eduler_Worker-1] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:10.002 INFO 38484 --- [eduler_Worker-1] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:10 CST 2021
2021-10-01 13:09:11.014 INFO 38484 --- [eduler_Worker-2] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
2021-10-01 13:09:11.014 INFO 38484 --- [eduler_Worker-2] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:11 CST 2021

实现案例 - 分布式方式

本例将展示quartz实现基于数据库的分布式任务管理,和控制job生命周期。

整体项目结构如下:

后端实现

  • 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">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <modelVersion>4.0.0</modelVersion>
<groupId>tech.pdai</groupId>
<artifactId>423-springboot-demo-schedule-quartz-cluster</artifactId>
<version>1.0-SNAPSHOT</version> <properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version><!--$NO-MVN-MAN-VER$-->
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<optional>true</optional>
</dependency> <dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies> </project>
  • 创建Schema

需要提前在MySQL中创建schema: quartz_jobs

# 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;
# DROP TABLE IF EXISTS QRTZ_TASK_HISTORY; 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))
ENGINE=InnoDB; 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))
ENGINE=InnoDB; 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))
ENGINE=InnoDB; 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(120) 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))
ENGINE=InnoDB; 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))
ENGINE=InnoDB; 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),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB; 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))
ENGINE=InnoDB; CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB; 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))
ENGINE=InnoDB; 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))
ENGINE=InnoDB; CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB; CREATE TABLE QRTZ_TASK_HISTORY (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_ID VARCHAR(200) NOT NULL,
FIRE_ID VARCHAR(95) NOT NULL,
TASK_NAME VARCHAR(200) NULL,
TASK_GROUP VARCHAR(200) NULL,
FIRED_TIME BIGINT(13) NULL,
FIRED_WAY VARCHAR(8) NULL,
COMPLETE_TIME BIGINT(13) NULL,
EXPEND_TIME BIGINT(13) NULL,
REFIRED INT NULL,
EXEC_STATE VARCHAR(10) NULL,
LOG TEXT NULL,
PRIMARY KEY (FIRE_ID)
)ENGINE=InnoDB; CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_TK_S ON QRTZ_TASK_HISTORY(SCHED_NAME); commit;
  • application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/quartz_jobs?useUnicode=true&useSSL=false
username: root
password: xxxxxxxx
driver-class-name: com.mysql.jdbc.Driver
quartz:
#相关属性配置
properties:
org:
quartz:
scheduler:
instanceName: clusteredScheduler
instanceId: AUTO
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: true
clusterCheckinInterval: 10000
useProperties: false
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
#数据库方式
job-store-type: jdbc
  • 定义JobDetails实体
/**
* @author pdai
*
*/
@Data
public class JobDetails{
private String cronExpression;
private String jobClassName;
private String triggerGroupName;
private String triggerName;
private String jobGroupName;
private String jobName;
private Date nextFireTime;
private Date previousFireTime;
private Date startTime;
private String timeZone;
private String status;
}
  • Job管理类
package tech.pdai.springboot.quartz.cluster.manager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set; import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.DateBuilder;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import tech.pdai.springboot.quartz.cluster.entity.JobDetails; /**
* @author pdai
*/
@Component
public class QuartzManager { @Autowired
private Scheduler sched; /**
* 创建or更新任务,存在则更新不存在创建
*
* @param jobClass 任务类
* @param jobName 任务名称
* @param jobGroupName 任务组名称
* @param jobCron cron表达式
*/
public void addOrUpdateJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobCron) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger==null) {
addJob(jobClass, jobName, jobGroupName, jobCron);
} else {
if (trigger.getCronExpression().equals(jobCron)) {
return;
}
updateJob(jobName, jobGroupName, jobCron);
}
} catch (SchedulerException e) {
e.printStackTrace();
}
} /**
* 增加一个job
*
* @param jobClass 任务实现类
* @param jobName 任务名称
* @param jobGroupName 任务组名
* @param jobCron cron表达式(如:0/5 * * * * ? )
*/
public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobCron) {
try {
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
.startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
.withSchedule(CronScheduleBuilder.cronSchedule(jobCron)).startNow().build(); sched.scheduleJob(jobDetail, trigger);
if (!sched.isShutdown()) {
sched.start();
}
} catch (SchedulerException e) {
e.printStackTrace();
}
} /**
* @param jobClass
* @param jobName
* @param jobGroupName
* @param jobTime
*/
public void addJob(Class<? extends Job> jobClass, String jobName, String jobGroupName, int jobTime) {
addJob(jobClass, jobName, jobGroupName, jobTime, -1);
} public void addJob(Class<? extends Job> jobClass, String jobName, String jobGroupName, int jobTime, int jobTimes) {
try {
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)// 任务名称和组构成任务key
.build();
// 使用simpleTrigger规则
Trigger trigger;
if (jobTimes < 0) {
trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime))
.startNow().build();
} else {
trigger = TriggerBuilder
.newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder
.repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes))
.startNow().build();
}
sched.scheduleJob(jobDetail, trigger);
if (!sched.isShutdown()) {
sched.start();
}
} catch (SchedulerException e) {
e.printStackTrace();
}
} public void updateJob(String jobName, String jobGroupName, String jobTime) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
// 重启触发器
sched.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
} /**
* 删除任务一个job
*
* @param jobName 任务名称
* @param jobGroupName 任务组名
*/
public void deleteJob(String jobName, String jobGroupName) {
try {
sched.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
sched.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroupName));
sched.deleteJob(new JobKey(jobName, jobGroupName));
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 暂停一个job
*
* @param jobName
* @param jobGroupName
*/
public void pauseJob(String jobName, String jobGroupName) {
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
sched.pauseJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
} /**
* 恢复一个job
*
* @param jobName
* @param jobGroupName
*/
public void resumeJob(String jobName, String jobGroupName) {
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
sched.resumeJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
} /**
* 立即执行一个job
*
* @param jobName
* @param jobGroupName
*/
public void runAJobNow(String jobName, String jobGroupName) {
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
sched.triggerJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
} public PageInfo<JobDetails> queryAllJobBean(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<JobDetails> jobList = null;
try {
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = sched.getJobKeys(matcher);
jobList = new ArrayList<>();
for (JobKey jobKey : jobKeys) {
List<? extends Trigger> triggers = sched.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
JobDetails jobDetails = new JobDetails();
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
jobDetails.setCronExpression(cronTrigger.getCronExpression());
jobDetails.setTimeZone(cronTrigger.getTimeZone().getDisplayName());
}
jobDetails.setTriggerGroupName(trigger.getKey().getName());
jobDetails.setTriggerName(trigger.getKey().getGroup());
jobDetails.setJobGroupName(jobKey.getGroup());
jobDetails.setJobName(jobKey.getName());
jobDetails.setStartTime(trigger.getStartTime());
jobDetails.setJobClassName(sched.getJobDetail(jobKey).getJobClass().getName());
jobDetails.setNextFireTime(trigger.getNextFireTime());
jobDetails.setPreviousFireTime(trigger.getPreviousFireTime());
jobDetails.setStatus(sched.getTriggerState(trigger.getKey()).name());
jobList.add(jobDetails);
}
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return new PageInfo<>(jobList);
} /**
* 获取所有计划中的任务列表
*
* @return
*/
public List<Map<String, Object>> queryAllJob() {
List<Map<String, Object>> jobList = null;
try {
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = sched.getJobKeys(matcher);
jobList = new ArrayList<>();
for (JobKey jobKey : jobKeys) {
List<? extends Trigger> triggers = sched.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
Map<String, Object> map = new HashMap<>();
map.put("jobName", jobKey.getName());
map.put("jobGroupName", jobKey.getGroup());
map.put("description", "trigger:" + trigger.getKey());
Trigger.TriggerState triggerState = sched.getTriggerState(trigger.getKey());
map.put("jobStatus", triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
map.put("jobTime", cronExpression);
}
jobList.add(map);
}
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return jobList;
} /**
* 获取所有正在运行的job
*
* @return
*/
public List<Map<String, Object>> queryRunJon() {
List<Map<String, Object>> jobList = null;
try {
List<JobExecutionContext> executingJobs = sched.getCurrentlyExecutingJobs();
jobList = new ArrayList<>(executingJobs.size());
for (JobExecutionContext executingJob : executingJobs) {
Map<String, Object> map = new HashMap<>();
JobDetail jobDetail = executingJob.getJobDetail();
JobKey jobKey = jobDetail.getKey();
Trigger trigger = executingJob.getTrigger();
map.put("jobName", jobKey.getName());
map.put("jobGroupName", jobKey.getGroup());
map.put("description", "trigger:" + trigger.getKey());
Trigger.TriggerState triggerState = sched.getTriggerState(trigger.getKey());
map.put("jobStatus", triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
map.put("jobTime", cronExpression);
}
jobList.add(map);
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return jobList;
}
}
  • Job控制器接口
package tech.pdai.springboot.quartz.cluster.controller;

import java.util.HashMap;
import java.util.Map; import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import tech.pdai.springboot.quartz.cluster.entity.JobDetails;
import tech.pdai.springboot.quartz.cluster.manager.QuartzManager; /**
* @author pdai
*/
@RestController
@RequestMapping(value = "/job")
public class JobController { @Autowired
private QuartzManager qtzManager; @SuppressWarnings("unchecked")
private static Class<? extends QuartzJobBean> getClass(String classname) throws Exception {
Class<?> class1 = Class.forName(classname);
return (Class<? extends QuartzJobBean>) class1;
} /**
* @param jobClassName
* @param jobGroupName
* @param cronExpression
* @throws Exception
*/
@PostMapping(value = "/addjob")
public void addjob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName,
@RequestParam(value = "cronExpression") String cronExpression) throws Exception {
qtzManager.addOrUpdateJob(getClass(jobClassName), jobClassName, jobGroupName, cronExpression);
} /**
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value = "/pausejob")
public void pausejob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
qtzManager.pauseJob(jobClassName, jobGroupName);
} /**
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value = "/resumejob")
public void resumejob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
qtzManager.resumeJob(jobClassName, jobGroupName);
} /**
* @param jobClassName
* @param jobGroupName
* @param cronExpression
* @throws Exception
*/
@PostMapping(value = "/reschedulejob")
public void rescheduleJob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName,
@RequestParam(value = "cronExpression") String cronExpression) throws Exception {
qtzManager.addOrUpdateJob(getClass(jobClassName), jobClassName, jobGroupName, cronExpression);
} /**
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value = "/deletejob")
public void deletejob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
qtzManager.deleteJob(jobClassName, jobGroupName);
} /**
* @param pageNum
* @param pageSize
* @return
*/
@GetMapping(value = "/queryjob")
public Map<String, Object> queryjob(@RequestParam(value = "pageNum") Integer pageNum,
@RequestParam(value = "pageSize") Integer pageSize) {
PageInfo<JobDetails> jobAndTrigger = qtzManager.queryAllJobBean(pageNum, pageSize);
Map<String, Object> map = new HashMap<String, Object>();
map.put("JobAndTrigger", jobAndTrigger);
map.put("number", jobAndTrigger.getTotal());
return map;
}
}
  • 定义具体的Job
package tech.pdai.springboot.quartz.cluster.job;

import java.util.Date;

import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean; @Slf4j
public class HelloJob extends QuartzJobBean { @Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// get parameters
context.getJobDetail().getJobDataMap().forEach(
(k, v) -> log.info("param, key:{}, value:{}", k, v)
);
// your logics
log.info("Hello Job执行时间: " + new Date());
}
}

前端实现

简单用VueJS 写个页面测试

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>QuartzDemo</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.0.5/lib/theme-chalk/index.css">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="http://cdn.bootcss.com/vue-resource/1.3.4/vue-resource.js"></script>
<script src="https://unpkg.com/element-ui@2.0.5/lib/index.js"></script> <style>
#top {
background:#20A0FF;
padding:5px;
overflow:hidden
}
</style> </head>
<body>
<div id="test"> <div id="top">
<el-button type="text" @click="search" style="color:white">查询</el-button>
<el-button type="text" @click="handleadd" style="color:white">添加</el-button>
</span>
</div> <br/> <div style="margin-top:15px"> <el-table
ref="testTable"
:data="tableData"
style="width:100%"
border
>
<el-table-column
prop="status"
label="任务状态"
sortable
show-overflow-tooltip>
</el-table-column> <el-table-column
prop="jobName"
label="任务名称"
sortable
show-overflow-tooltip>
</el-table-column> <el-table-column
prop="jobGroupName"
label="任务所在组"
sortable>
</el-table-column> <el-table-column
prop="jobClassName"
label="任务类名"
sortable>
</el-table-column> <el-table-column
prop="triggerName"
label="触发器名称"
sortable>
</el-table-column> <el-table-column
prop="triggerGroupName"
label="触发器所在组"
sortable>
</el-table-column> <el-table-column
prop="cronExpression"
label="表达式"
sortable>
</el-table-column> <el-table-column
prop="timeZone"
label="时区"
sortable>
</el-table-column> <el-table-column label="操作" width="300">
<template scope="scope">
<el-button
size="small"
type="warning"
@click="handlePause(scope.$index, scope.row)">暂停</el-button> <el-button
size="small"
type="info"
@click="handleResume(scope.$index, scope.row)">恢复</el-button> <el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</el-button> <el-button
size="small"
type="success"
@click="handleUpdate(scope.$index, scope.row)">修改</el-button>
</template>
</el-table-column>
</el-table> <div align="center">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="totalCount">
</el-pagination>
</div>
</div> <el-dialog title="添加任务" :visible.sync="dialogFormVisible">
<el-form :model="form">
<el-form-item label="任务名称" label-width="120px" style="width:35%">
<el-input v-model="form.jobName" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="任务分组" label-width="120px" style="width:35%">
<el-input v-model="form.jobGroup" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="表达式" label-width="120px" style="width:35%">
<el-input v-model="form.cronExpression" auto-complete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="add">确 定</el-button>
</div>
</el-dialog> <el-dialog title="修改任务" :visible.sync="updateFormVisible">
<el-form :model="updateform">
<el-form-item label="表达式" label-width="120px" style="width:35%">
<el-input v-model="updateform.cronExpression" auto-complete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="updateFormVisible = false">取 消</el-button>
<el-button type="primary" @click="update">确 定</el-button>
</div>
</el-dialog> </div> <footer align="center">
<p>&copy; Quartz 任务管理</p>
</footer> <script>
var vue = new Vue({
el:"#test",
data: {
//表格当前页数据
tableData: [], //请求的URL
url:'job/queryjob', //默认每页数据量
pagesize: 10, //当前页码
currentPage: 1, //查询的页码
start: 1, //默认数据总数
totalCount: 1000, //添加对话框默认可见性
dialogFormVisible: false, //修改对话框默认可见性
updateFormVisible: false, //提交的表单
form: {
jobName: '',
jobGroup: '',
cronExpression: '',
}, updateform: {
jobName: '',
jobGroup: '',
cronExpression: '',
},
}, methods: { //从服务器读取数据
loadData: function(pageNum, pageSize){
this.$http.get('job/queryjob?' + 'pageNum=' + pageNum + '&pageSize=' + pageSize).then(function(res){
console.log(res)
this.tableData = res.body.JobAndTrigger.list;
this.totalCount = res.body.number;
},function(){
console.log('failed');
});
}, //单行删除
handleDelete: function(index, row) {
this.$http.post('job/deletejob',{"jobClassName":row.jobName,"jobGroupName":row.jobGroupName},{emulateJSON: true}).then(function(res){
this.loadData( this.currentPage, this.pagesize);
},function(){
console.log('failed');
});
}, //暂停任务
handlePause: function(index, row){
this.$http.post('job/pausejob',{"jobClassName":row.jobName,"jobGroupName":row.jobGroupName},{emulateJSON: true}).then(function(res){
this.loadData( this.currentPage, this.pagesize);
},function(){
console.log('failed');
});
}, //恢复任务
handleResume: function(index, row){
this.$http.post('job/resumejob',{"jobClassName":row.jobName,"jobGroupName":row.jobGroupName},{emulateJSON: true}).then(function(res){
this.loadData( this.currentPage, this.pagesize);
},function(){
console.log('failed');
});
}, //搜索
search: function(){
this.loadData(this.currentPage, this.pagesize);
}, //弹出对话框
handleadd: function(){
this.dialogFormVisible = true;
}, //添加
add: function(){
this.$http.post('job/addjob',{"jobClassName":this.form.jobName,"jobGroupName":this.form.jobGroup,"cronExpression":this.form.cronExpression},{emulateJSON: true}).then(function(res){
this.loadData(this.currentPage, this.pagesize);
this.dialogFormVisible = false;
},function(){
console.log('failed');
});
}, //更新
handleUpdate: function(index, row){
console.log(row)
this.updateFormVisible = true;
this.updateform.jobName = row.jobClassName;
this.updateform.jobGroup = row.jobGroupName;
}, //更新任务
update: function(){
this.$http.post
('job/reschedulejob',
{"jobClassName":this.updateform.jobName,
"jobGroupName":this.updateform.jobGroup,
"cronExpression":this.updateform.cronExpression
},{emulateJSON: true}
).then(function(res){
this.loadData(this.currentPage, this.pagesize);
this.updateFormVisible = false;
},function(){
console.log('failed');
}); }, //每页显示数据量变更
handleSizeChange: function(val) {
this.pagesize = val;
this.loadData(this.currentPage, this.pagesize);
}, //页码变更
handleCurrentChange: function(val) {
this.currentPage = val;
this.loadData(this.currentPage, this.pagesize);
}, }, }); //载入数据
vue.loadData(vue.currentPage, vue.pagesize);
</script> </body>
</html>

测试效果

(PS: 这里的任务名称需要改成你自己的完整类名称)

展示正在运行的Jobs:

增加新的Job:

Jobs持久化在数据库:

示例源码

https://github.com/realpdai/tech-pdai-spring-demos

更多内容

告别碎片化学习,无套路一站式体系化学习后端开发: Java 全栈知识体系 https://pdai.tech

SpringBoot定时任务 - 集成quartz实现定时任务(单实例和分布式两种方式)的更多相关文章

  1. C++实现程序单实例运行的两种方式

    简介 在我们编写程序的时候,经常会注意到的一个问题就是如何能够让程序只运行一个实例,确保不会让同一个程序多次运行,从而产生诸多相同进程,给我们的带来不便呢?那么常用的有以下四种方法,第一种方法是通过扫 ...

  2. iOS:创建单例对象的两种方式

    单例模式:创建单例对象的两种方式 方式一:iOS4版本之前      static SingleClassManager *singleManager = nil;      +(SingleClas ...

  3. springboot项目启动成功后执行一段代码的两种方式

    springboot项目启动成功后执行一段代码的两种方式 实现ApplicationRunner接口 package com.lnjecit.lifecycle; import org.springf ...

  4. Java中 单例(Singleton)的两种方式

    第一种(饿汉式单例模式):在声明变量时实例化 public class Singleton { //静态初始化自动实例化 private static Singleton instance = new ...

  5. 浏览器原生 form 表单POST 数据的两种方式

    我们在提交表单的时候,form表单参数中会有一个enctype的参数.enctype指定了HTTP请求的Content-Type. 常用有两种:application/x-www-form-urlen ...

  6. Spring Boot集成quartz实现定时任务并支持切换任务数据源

    org.quartz实现定时任务并自定义切换任务数据源 在工作中经常会需要使用到定时任务处理各种周期性的任务,org.quartz是处理此类定时任务的一个优秀框架.随着项目一点点推进,此时我们并不满足 ...

  7. Spring Boot 中实现定时任务的两种方式

    在 Spring + SpringMVC 环境中,一般来说,要实现定时任务,我们有两中方案,一种是使用 Spring 自带的定时任务处理器 @Scheduled 注解,另一种就是使用第三方框架 Qua ...

  8. Spring集成Quartz框架的两种方式。

    可参考:https://blog.csdn.net/yk614294861/article/details/84324603 1.使用Spring与Quarta配置作业得两种方式: a.方式一,Met ...

  9. [HBase Manual]CH5 HBase运行模式:单实例和分布式

    HBase运行模式:单实例和分布式 HBase运行模式:单实例和分布式 1.单实例模式 1.1 单实例在HDFS下 2.分布式 2.1 伪分布式 3完全分布式 HBase有2种运行模式,单实例和分布式 ...

随机推荐

  1. 从 rails 窥探 web 全栈开发(零)

    从 rails 窥探 web 全栈开发(零) 本文将讲述在学习之前几个必须要知道的概念,这些词汇在 rails 中都会出现. 本文前置条件:安装好 Ruby. 从 rails 窥探 web 全栈开发( ...

  2. 445. Add Two Numbers II - LeetCode

    Question 445. Add Two Numbers II Solution 题目大意:两个列表相加 思路:构造两个栈,两个列表的数依次入栈,再出栈的时候计算其和作为返回链表的一个节点 Java ...

  3. 协议层安全相关《http请求走私与CTF利用》

    0x00 前言 最近刷题的时候多次遇到HTTP请求走私相关的题目,但之前都没怎么接触到相关的知识点,只是在GKCTF2021--hackme中使用到了 CVE-2019-20372(Nginx< ...

  4. 《C Primer Plus》第六版笔记--4~6章

    目录 第四章 字符串和格式化输入/输出 第五章 运算符.表达式和语句 第六章 C控制语句:循环 虽然匆匆忙忙,但还是要做笔记,虽然大概都知道...... 挑一些容易忘记的地方 第四章 字符串和格式化输 ...

  5. Navicat 连接 MySQL

    目录 简述 新建连接 常见错误 简述 Navicat 是一套快速.可靠和全面的数据库管理工具,专门用于简化数据库管理和降低管理成本.Navicat 图形界面直观,提供简便的管理方法,设计和操作 MyS ...

  6. 直接将A类库复制到vs中的B类库,但是解决方案菜单中不显示

    1.将要复制的文件夹复制粘贴到你要用的vs项目中 2.右键 添加   现有项目  选中xxxxx.csproj文件   点击  打开   就可以了

  7. php 使用phpqrcode生成二维码并上传到OSS

    一般情况调用phpqrcode第三方插件 会把生成的二维码图片保存到服务器,不保存服务器也会以header头的形式输出到浏览器,(我们不允许把图片文件保存的liunx服务器,只能保存到阿里云OSS存储 ...

  8. 【Redis】哨兵初始化和主观下线

    在的redis启动函数main(server.c文件)中,对哨兵模式进行了检查,如果是哨兵模式,将调用initSentinelConfig和initSentinel进行初始化,initServer函数 ...

  9. 二、shell 脚本条件测试

    目录 一.条件测试 test 格式 文件测试 文件测试常见选项 整数值比较 字符串比较 浮点数的运算 逻辑测试 二.if语句 1单分支 2双分支结构 3多分支结构 三元运算符 三.case 一.条件测 ...

  10. OpenSSF安全计划:SBOM将驱动软件供应链安全

    在 软件成分分析(SCA)一文中,我们简单提到软件物料清单(SBOM)在安全实践中的价值. 本期文章将带你深入了解 "SBOM 无处不在"计划是什么,以及 SBOM 对未来软件供应 ...