最为常用定时任务框架是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依赖
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-quartz</artifactId>
  4. </dependency>
  • 定义Job

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

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

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

  1. /**
  2. * @author pdai
  3. */
  4. @Configuration
  5. public class QuartzConfig {
  6. @Bean("helloJob")
  7. public JobDetail helloJobDetail() {
  8. return JobBuilder.newJob(HelloJob.class)
  9. .withIdentity("DateTimeJob")
  10. .usingJobData("msg", "Hello Quartz")
  11. .storeDurably()//即使没有Trigger关联时,也不需要删除该JobDetail
  12. .build();
  13. }
  14. @Bean
  15. public Trigger printTimeJobTrigger() {
  16. // 每秒执行一次
  17. CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ?");
  18. return TriggerBuilder.newTrigger()
  19. .forJob(helloJobDetail())
  20. .withIdentity("quartzTaskService")
  21. .withSchedule(cronScheduleBuilder)
  22. .build();
  23. }
  24. }
  • 执行测试
  1. 2021-10-01 13:09:00.380 INFO 38484 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
  2. 2021-10-01 13:09:00.391 INFO 38484 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
  3. 2021-10-01 13:09:00.392 INFO 38484 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.50]
  4. 2021-10-01 13:09:00.526 INFO 38484 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
  5. 2021-10-01 13:09:00.526 INFO 38484 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1424 ms
  6. 2021-10-01 13:09:00.866 INFO 38484 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
  7. 2021-10-01 13:09:00.877 INFO 38484 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
  8. 2021-10-01 13:09:00.877 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
  9. 2021-10-01 13:09:00.878 INFO 38484 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
  10. 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'
  11. Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  12. NOT STARTED.
  13. Currently in standby mode.
  14. Number of jobs executed: 0
  15. Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  16. Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
  17. 2021-10-01 13:09:00.878 INFO 38484 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance.
  18. 2021-10-01 13:09:00.879 INFO 38484 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
  19. 2021-10-01 13:09:00.879 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@6075b2d3
  20. 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 ''
  21. 2021-10-01 13:09:00.923 INFO 38484 --- [ main] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now
  22. 2021-10-01 13:09:00.923 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started.
  23. 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)
  24. 2021-10-01 13:09:00.931 INFO 38484 --- [eduler_Worker-1] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  25. 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
  26. 2021-10-01 13:09:01.001 INFO 38484 --- [eduler_Worker-2] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  27. 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
  28. 2021-10-01 13:09:02.000 INFO 38484 --- [eduler_Worker-3] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  29. 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
  30. 2021-10-01 13:09:03.000 INFO 38484 --- [eduler_Worker-4] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  31. 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
  32. 2021-10-01 13:09:04.001 INFO 38484 --- [eduler_Worker-5] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  33. 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
  34. 2021-10-01 13:09:05.002 INFO 38484 --- [eduler_Worker-6] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  35. 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
  36. 2021-10-01 13:09:06.000 INFO 38484 --- [eduler_Worker-7] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  37. 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
  38. 2021-10-01 13:09:07.002 INFO 38484 --- [eduler_Worker-8] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  39. 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
  40. 2021-10-01 13:09:08.002 INFO 38484 --- [eduler_Worker-9] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  41. 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
  42. 2021-10-01 13:09:09.000 INFO 38484 --- [duler_Worker-10] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  43. 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
  44. 2021-10-01 13:09:10.001 INFO 38484 --- [eduler_Worker-1] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  45. 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
  46. 2021-10-01 13:09:11.014 INFO 38484 --- [eduler_Worker-2] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz
  47. 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
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.5.3</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <modelVersion>4.0.0</modelVersion>
  12. <groupId>tech.pdai</groupId>
  13. <artifactId>423-springboot-demo-schedule-quartz-cluster</artifactId>
  14. <version>1.0-SNAPSHOT</version>
  15. <properties>
  16. <maven.compiler.source>8</maven.compiler.source>
  17. <maven.compiler.target>8</maven.compiler.target>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-quartz</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-web</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter-data-jpa</artifactId>
  31. </dependency>
  32. <dependency>
  33. <groupId>mysql</groupId>
  34. <artifactId>mysql-connector-java</artifactId>
  35. <version>5.1.42</version><!--$NO-MVN-MAN-VER$-->
  36. <scope>runtime</scope>
  37. </dependency>
  38. <dependency>
  39. <groupId>org.springframework.boot</groupId>
  40. <artifactId>spring-boot-starter-test</artifactId>
  41. <scope>test</scope>
  42. </dependency>
  43. <dependency>
  44. <groupId>org.projectlombok</groupId>
  45. <artifactId>lombok</artifactId>
  46. <version>1.18.20</version>
  47. <optional>true</optional>
  48. </dependency>
  49. <dependency>
  50. <groupId>com.github.pagehelper</groupId>
  51. <artifactId>pagehelper</artifactId>
  52. <version>5.0.0</version>
  53. </dependency>
  54. </dependencies>
  55. </project>
  • 创建Schema

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

  1. # DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
  2. # DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
  3. # DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
  4. # DROP TABLE IF EXISTS QRTZ_LOCKS;
  5. # DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
  6. # DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
  7. # DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
  8. # DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
  9. # DROP TABLE IF EXISTS QRTZ_TRIGGERS;
  10. # DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
  11. # DROP TABLE IF EXISTS QRTZ_CALENDARS;
  12. # DROP TABLE IF EXISTS QRTZ_TASK_HISTORY;
  13. CREATE TABLE QRTZ_JOB_DETAILS(
  14. SCHED_NAME VARCHAR(120) NOT NULL,
  15. JOB_NAME VARCHAR(200) NOT NULL,
  16. JOB_GROUP VARCHAR(200) NOT NULL,
  17. DESCRIPTION VARCHAR(250) NULL,
  18. JOB_CLASS_NAME VARCHAR(250) NOT NULL,
  19. IS_DURABLE VARCHAR(1) NOT NULL,
  20. IS_NONCONCURRENT VARCHAR(1) NOT NULL,
  21. IS_UPDATE_DATA VARCHAR(1) NOT NULL,
  22. REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
  23. JOB_DATA BLOB NULL,
  24. PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
  25. ENGINE=InnoDB;
  26. CREATE TABLE QRTZ_TRIGGERS (
  27. SCHED_NAME VARCHAR(120) NOT NULL,
  28. TRIGGER_NAME VARCHAR(200) NOT NULL,
  29. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  30. JOB_NAME VARCHAR(200) NOT NULL,
  31. JOB_GROUP VARCHAR(200) NOT NULL,
  32. DESCRIPTION VARCHAR(250) NULL,
  33. NEXT_FIRE_TIME BIGINT(13) NULL,
  34. PREV_FIRE_TIME BIGINT(13) NULL,
  35. PRIORITY INTEGER NULL,
  36. TRIGGER_STATE VARCHAR(16) NOT NULL,
  37. TRIGGER_TYPE VARCHAR(8) NOT NULL,
  38. START_TIME BIGINT(13) NOT NULL,
  39. END_TIME BIGINT(13) NULL,
  40. CALENDAR_NAME VARCHAR(200) NULL,
  41. MISFIRE_INSTR SMALLINT(2) NULL,
  42. JOB_DATA BLOB NULL,
  43. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  44. FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
  45. REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
  46. ENGINE=InnoDB;
  47. CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
  48. SCHED_NAME VARCHAR(120) NOT NULL,
  49. TRIGGER_NAME VARCHAR(200) NOT NULL,
  50. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  51. REPEAT_COUNT BIGINT(7) NOT NULL,
  52. REPEAT_INTERVAL BIGINT(12) NOT NULL,
  53. TIMES_TRIGGERED BIGINT(10) NOT NULL,
  54. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  55. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  56. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  57. ENGINE=InnoDB;
  58. CREATE TABLE QRTZ_CRON_TRIGGERS (
  59. SCHED_NAME VARCHAR(120) NOT NULL,
  60. TRIGGER_NAME VARCHAR(200) NOT NULL,
  61. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  62. CRON_EXPRESSION VARCHAR(120) NOT NULL,
  63. TIME_ZONE_ID VARCHAR(80),
  64. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  65. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  66. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  67. ENGINE=InnoDB;
  68. CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  69. (
  70. SCHED_NAME VARCHAR(120) NOT NULL,
  71. TRIGGER_NAME VARCHAR(200) NOT NULL,
  72. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  73. STR_PROP_1 VARCHAR(512) NULL,
  74. STR_PROP_2 VARCHAR(512) NULL,
  75. STR_PROP_3 VARCHAR(512) NULL,
  76. INT_PROP_1 INT NULL,
  77. INT_PROP_2 INT NULL,
  78. LONG_PROP_1 BIGINT NULL,
  79. LONG_PROP_2 BIGINT NULL,
  80. DEC_PROP_1 NUMERIC(13,4) NULL,
  81. DEC_PROP_2 NUMERIC(13,4) NULL,
  82. BOOL_PROP_1 VARCHAR(1) NULL,
  83. BOOL_PROP_2 VARCHAR(1) NULL,
  84. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  85. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  86. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  87. ENGINE=InnoDB;
  88. CREATE TABLE QRTZ_BLOB_TRIGGERS (
  89. SCHED_NAME VARCHAR(120) NOT NULL,
  90. TRIGGER_NAME VARCHAR(200) NOT NULL,
  91. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  92. BLOB_DATA BLOB NULL,
  93. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  94. INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
  95. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  96. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  97. ENGINE=InnoDB;
  98. CREATE TABLE QRTZ_CALENDARS (
  99. SCHED_NAME VARCHAR(120) NOT NULL,
  100. CALENDAR_NAME VARCHAR(200) NOT NULL,
  101. CALENDAR BLOB NOT NULL,
  102. PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
  103. ENGINE=InnoDB;
  104. CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
  105. SCHED_NAME VARCHAR(120) NOT NULL,
  106. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  107. PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
  108. ENGINE=InnoDB;
  109. CREATE TABLE QRTZ_FIRED_TRIGGERS (
  110. SCHED_NAME VARCHAR(120) NOT NULL,
  111. ENTRY_ID VARCHAR(95) NOT NULL,
  112. TRIGGER_NAME VARCHAR(200) NOT NULL,
  113. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  114. INSTANCE_NAME VARCHAR(200) NOT NULL,
  115. FIRED_TIME BIGINT(13) NOT NULL,
  116. SCHED_TIME BIGINT(13) NOT NULL,
  117. PRIORITY INTEGER NOT NULL,
  118. STATE VARCHAR(16) NOT NULL,
  119. JOB_NAME VARCHAR(200) NULL,
  120. JOB_GROUP VARCHAR(200) NULL,
  121. IS_NONCONCURRENT VARCHAR(1) NULL,
  122. REQUESTS_RECOVERY VARCHAR(1) NULL,
  123. PRIMARY KEY (SCHED_NAME,ENTRY_ID))
  124. ENGINE=InnoDB;
  125. CREATE TABLE QRTZ_SCHEDULER_STATE (
  126. SCHED_NAME VARCHAR(120) NOT NULL,
  127. INSTANCE_NAME VARCHAR(200) NOT NULL,
  128. LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
  129. CHECKIN_INTERVAL BIGINT(13) NOT NULL,
  130. PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
  131. ENGINE=InnoDB;
  132. CREATE TABLE QRTZ_LOCKS (
  133. SCHED_NAME VARCHAR(120) NOT NULL,
  134. LOCK_NAME VARCHAR(40) NOT NULL,
  135. PRIMARY KEY (SCHED_NAME,LOCK_NAME))
  136. ENGINE=InnoDB;
  137. CREATE TABLE QRTZ_TASK_HISTORY (
  138. SCHED_NAME VARCHAR(120) NOT NULL,
  139. INSTANCE_ID VARCHAR(200) NOT NULL,
  140. FIRE_ID VARCHAR(95) NOT NULL,
  141. TASK_NAME VARCHAR(200) NULL,
  142. TASK_GROUP VARCHAR(200) NULL,
  143. FIRED_TIME BIGINT(13) NULL,
  144. FIRED_WAY VARCHAR(8) NULL,
  145. COMPLETE_TIME BIGINT(13) NULL,
  146. EXPEND_TIME BIGINT(13) NULL,
  147. REFIRED INT NULL,
  148. EXEC_STATE VARCHAR(10) NULL,
  149. LOG TEXT NULL,
  150. PRIMARY KEY (FIRE_ID)
  151. )ENGINE=InnoDB;
  152. CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
  153. CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
  154. CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
  155. CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
  156. CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
  157. CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
  158. CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
  159. CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
  160. CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
  161. CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
  162. CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
  163. CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
  164. CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
  165. CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
  166. CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
  167. CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
  168. CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
  169. CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
  170. CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
  171. CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
  172. CREATE INDEX IDX_QRTZ_TK_S ON QRTZ_TASK_HISTORY(SCHED_NAME);
  173. commit;
  • application.yml
  1. spring:
  2. datasource:
  3. url: jdbc:mysql://localhost:3306/quartz_jobs?useUnicode=true&useSSL=false
  4. username: root
  5. password: xxxxxxxx
  6. driver-class-name: com.mysql.jdbc.Driver
  7. quartz:
  8. #相关属性配置
  9. properties:
  10. org:
  11. quartz:
  12. scheduler:
  13. instanceName: clusteredScheduler
  14. instanceId: AUTO
  15. jobStore:
  16. class: org.quartz.impl.jdbcjobstore.JobStoreTX
  17. driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  18. tablePrefix: QRTZ_
  19. isClustered: true
  20. clusterCheckinInterval: 10000
  21. useProperties: false
  22. threadPool:
  23. class: org.quartz.simpl.SimpleThreadPool
  24. threadCount: 10
  25. threadPriority: 5
  26. threadsInheritContextClassLoaderOfInitializingThread: true
  27. #数据库方式
  28. job-store-type: jdbc
  • 定义JobDetails实体
  1. /**
  2. * @author pdai
  3. *
  4. */
  5. @Data
  6. public class JobDetails{
  7. private String cronExpression;
  8. private String jobClassName;
  9. private String triggerGroupName;
  10. private String triggerName;
  11. private String jobGroupName;
  12. private String jobName;
  13. private Date nextFireTime;
  14. private Date previousFireTime;
  15. private Date startTime;
  16. private String timeZone;
  17. private String status;
  18. }
  • Job管理类
  1. package tech.pdai.springboot.quartz.cluster.manager;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import java.util.Map;
  6. import java.util.Set;
  7. import com.github.pagehelper.PageHelper;
  8. import com.github.pagehelper.PageInfo;
  9. import org.quartz.CronScheduleBuilder;
  10. import org.quartz.CronTrigger;
  11. import org.quartz.DateBuilder;
  12. import org.quartz.DateBuilder.IntervalUnit;
  13. import org.quartz.Job;
  14. import org.quartz.JobBuilder;
  15. import org.quartz.JobDetail;
  16. import org.quartz.JobExecutionContext;
  17. import org.quartz.JobKey;
  18. import org.quartz.Scheduler;
  19. import org.quartz.SchedulerException;
  20. import org.quartz.SimpleScheduleBuilder;
  21. import org.quartz.Trigger;
  22. import org.quartz.TriggerBuilder;
  23. import org.quartz.TriggerKey;
  24. import org.quartz.impl.matchers.GroupMatcher;
  25. import org.springframework.beans.factory.annotation.Autowired;
  26. import org.springframework.scheduling.quartz.QuartzJobBean;
  27. import org.springframework.stereotype.Component;
  28. import tech.pdai.springboot.quartz.cluster.entity.JobDetails;
  29. /**
  30. * @author pdai
  31. */
  32. @Component
  33. public class QuartzManager {
  34. @Autowired
  35. private Scheduler sched;
  36. /**
  37. * 创建or更新任务,存在则更新不存在创建
  38. *
  39. * @param jobClass 任务类
  40. * @param jobName 任务名称
  41. * @param jobGroupName 任务组名称
  42. * @param jobCron cron表达式
  43. */
  44. public void addOrUpdateJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobCron) {
  45. try {
  46. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
  47. CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
  48. if (trigger==null) {
  49. addJob(jobClass, jobName, jobGroupName, jobCron);
  50. } else {
  51. if (trigger.getCronExpression().equals(jobCron)) {
  52. return;
  53. }
  54. updateJob(jobName, jobGroupName, jobCron);
  55. }
  56. } catch (SchedulerException e) {
  57. e.printStackTrace();
  58. }
  59. }
  60. /**
  61. * 增加一个job
  62. *
  63. * @param jobClass 任务实现类
  64. * @param jobName 任务名称
  65. * @param jobGroupName 任务组名
  66. * @param jobCron cron表达式(如:0/5 * * * * ? )
  67. */
  68. public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobCron) {
  69. try {
  70. JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
  71. Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
  72. .startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
  73. .withSchedule(CronScheduleBuilder.cronSchedule(jobCron)).startNow().build();
  74. sched.scheduleJob(jobDetail, trigger);
  75. if (!sched.isShutdown()) {
  76. sched.start();
  77. }
  78. } catch (SchedulerException e) {
  79. e.printStackTrace();
  80. }
  81. }
  82. /**
  83. * @param jobClass
  84. * @param jobName
  85. * @param jobGroupName
  86. * @param jobTime
  87. */
  88. public void addJob(Class<? extends Job> jobClass, String jobName, String jobGroupName, int jobTime) {
  89. addJob(jobClass, jobName, jobGroupName, jobTime, -1);
  90. }
  91. public void addJob(Class<? extends Job> jobClass, String jobName, String jobGroupName, int jobTime, int jobTimes) {
  92. try {
  93. JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)// 任务名称和组构成任务key
  94. .build();
  95. // 使用simpleTrigger规则
  96. Trigger trigger;
  97. if (jobTimes < 0) {
  98. trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
  99. .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime))
  100. .startNow().build();
  101. } else {
  102. trigger = TriggerBuilder
  103. .newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder
  104. .repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes))
  105. .startNow().build();
  106. }
  107. sched.scheduleJob(jobDetail, trigger);
  108. if (!sched.isShutdown()) {
  109. sched.start();
  110. }
  111. } catch (SchedulerException e) {
  112. e.printStackTrace();
  113. }
  114. }
  115. public void updateJob(String jobName, String jobGroupName, String jobTime) {
  116. try {
  117. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
  118. CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
  119. trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
  120. .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
  121. // 重启触发器
  122. sched.rescheduleJob(triggerKey, trigger);
  123. } catch (SchedulerException e) {
  124. e.printStackTrace();
  125. }
  126. }
  127. /**
  128. * 删除任务一个job
  129. *
  130. * @param jobName 任务名称
  131. * @param jobGroupName 任务组名
  132. */
  133. public void deleteJob(String jobName, String jobGroupName) {
  134. try {
  135. sched.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
  136. sched.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroupName));
  137. sched.deleteJob(new JobKey(jobName, jobGroupName));
  138. } catch (Exception e) {
  139. e.printStackTrace();
  140. }
  141. }
  142. /**
  143. * 暂停一个job
  144. *
  145. * @param jobName
  146. * @param jobGroupName
  147. */
  148. public void pauseJob(String jobName, String jobGroupName) {
  149. try {
  150. JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
  151. sched.pauseJob(jobKey);
  152. } catch (SchedulerException e) {
  153. e.printStackTrace();
  154. }
  155. }
  156. /**
  157. * 恢复一个job
  158. *
  159. * @param jobName
  160. * @param jobGroupName
  161. */
  162. public void resumeJob(String jobName, String jobGroupName) {
  163. try {
  164. JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
  165. sched.resumeJob(jobKey);
  166. } catch (SchedulerException e) {
  167. e.printStackTrace();
  168. }
  169. }
  170. /**
  171. * 立即执行一个job
  172. *
  173. * @param jobName
  174. * @param jobGroupName
  175. */
  176. public void runAJobNow(String jobName, String jobGroupName) {
  177. try {
  178. JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
  179. sched.triggerJob(jobKey);
  180. } catch (SchedulerException e) {
  181. e.printStackTrace();
  182. }
  183. }
  184. public PageInfo<JobDetails> queryAllJobBean(int pageNum, int pageSize) {
  185. PageHelper.startPage(pageNum, pageSize);
  186. List<JobDetails> jobList = null;
  187. try {
  188. GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
  189. Set<JobKey> jobKeys = sched.getJobKeys(matcher);
  190. jobList = new ArrayList<>();
  191. for (JobKey jobKey : jobKeys) {
  192. List<? extends Trigger> triggers = sched.getTriggersOfJob(jobKey);
  193. for (Trigger trigger : triggers) {
  194. JobDetails jobDetails = new JobDetails();
  195. if (trigger instanceof CronTrigger) {
  196. CronTrigger cronTrigger = (CronTrigger) trigger;
  197. jobDetails.setCronExpression(cronTrigger.getCronExpression());
  198. jobDetails.setTimeZone(cronTrigger.getTimeZone().getDisplayName());
  199. }
  200. jobDetails.setTriggerGroupName(trigger.getKey().getName());
  201. jobDetails.setTriggerName(trigger.getKey().getGroup());
  202. jobDetails.setJobGroupName(jobKey.getGroup());
  203. jobDetails.setJobName(jobKey.getName());
  204. jobDetails.setStartTime(trigger.getStartTime());
  205. jobDetails.setJobClassName(sched.getJobDetail(jobKey).getJobClass().getName());
  206. jobDetails.setNextFireTime(trigger.getNextFireTime());
  207. jobDetails.setPreviousFireTime(trigger.getPreviousFireTime());
  208. jobDetails.setStatus(sched.getTriggerState(trigger.getKey()).name());
  209. jobList.add(jobDetails);
  210. }
  211. }
  212. } catch (SchedulerException e) {
  213. e.printStackTrace();
  214. }
  215. return new PageInfo<>(jobList);
  216. }
  217. /**
  218. * 获取所有计划中的任务列表
  219. *
  220. * @return
  221. */
  222. public List<Map<String, Object>> queryAllJob() {
  223. List<Map<String, Object>> jobList = null;
  224. try {
  225. GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
  226. Set<JobKey> jobKeys = sched.getJobKeys(matcher);
  227. jobList = new ArrayList<>();
  228. for (JobKey jobKey : jobKeys) {
  229. List<? extends Trigger> triggers = sched.getTriggersOfJob(jobKey);
  230. for (Trigger trigger : triggers) {
  231. Map<String, Object> map = new HashMap<>();
  232. map.put("jobName", jobKey.getName());
  233. map.put("jobGroupName", jobKey.getGroup());
  234. map.put("description", "trigger:" + trigger.getKey());
  235. Trigger.TriggerState triggerState = sched.getTriggerState(trigger.getKey());
  236. map.put("jobStatus", triggerState.name());
  237. if (trigger instanceof CronTrigger) {
  238. CronTrigger cronTrigger = (CronTrigger) trigger;
  239. String cronExpression = cronTrigger.getCronExpression();
  240. map.put("jobTime", cronExpression);
  241. }
  242. jobList.add(map);
  243. }
  244. }
  245. } catch (SchedulerException e) {
  246. e.printStackTrace();
  247. }
  248. return jobList;
  249. }
  250. /**
  251. * 获取所有正在运行的job
  252. *
  253. * @return
  254. */
  255. public List<Map<String, Object>> queryRunJon() {
  256. List<Map<String, Object>> jobList = null;
  257. try {
  258. List<JobExecutionContext> executingJobs = sched.getCurrentlyExecutingJobs();
  259. jobList = new ArrayList<>(executingJobs.size());
  260. for (JobExecutionContext executingJob : executingJobs) {
  261. Map<String, Object> map = new HashMap<>();
  262. JobDetail jobDetail = executingJob.getJobDetail();
  263. JobKey jobKey = jobDetail.getKey();
  264. Trigger trigger = executingJob.getTrigger();
  265. map.put("jobName", jobKey.getName());
  266. map.put("jobGroupName", jobKey.getGroup());
  267. map.put("description", "trigger:" + trigger.getKey());
  268. Trigger.TriggerState triggerState = sched.getTriggerState(trigger.getKey());
  269. map.put("jobStatus", triggerState.name());
  270. if (trigger instanceof CronTrigger) {
  271. CronTrigger cronTrigger = (CronTrigger) trigger;
  272. String cronExpression = cronTrigger.getCronExpression();
  273. map.put("jobTime", cronExpression);
  274. }
  275. jobList.add(map);
  276. }
  277. } catch (SchedulerException e) {
  278. e.printStackTrace();
  279. }
  280. return jobList;
  281. }
  282. }
  • Job控制器接口
  1. package tech.pdai.springboot.quartz.cluster.controller;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import com.github.pagehelper.PageInfo;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.scheduling.quartz.QuartzJobBean;
  7. import org.springframework.web.bind.annotation.GetMapping;
  8. import org.springframework.web.bind.annotation.PostMapping;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.bind.annotation.RequestParam;
  11. import org.springframework.web.bind.annotation.RestController;
  12. import tech.pdai.springboot.quartz.cluster.entity.JobDetails;
  13. import tech.pdai.springboot.quartz.cluster.manager.QuartzManager;
  14. /**
  15. * @author pdai
  16. */
  17. @RestController
  18. @RequestMapping(value = "/job")
  19. public class JobController {
  20. @Autowired
  21. private QuartzManager qtzManager;
  22. @SuppressWarnings("unchecked")
  23. private static Class<? extends QuartzJobBean> getClass(String classname) throws Exception {
  24. Class<?> class1 = Class.forName(classname);
  25. return (Class<? extends QuartzJobBean>) class1;
  26. }
  27. /**
  28. * @param jobClassName
  29. * @param jobGroupName
  30. * @param cronExpression
  31. * @throws Exception
  32. */
  33. @PostMapping(value = "/addjob")
  34. public void addjob(@RequestParam(value = "jobClassName") String jobClassName,
  35. @RequestParam(value = "jobGroupName") String jobGroupName,
  36. @RequestParam(value = "cronExpression") String cronExpression) throws Exception {
  37. qtzManager.addOrUpdateJob(getClass(jobClassName), jobClassName, jobGroupName, cronExpression);
  38. }
  39. /**
  40. * @param jobClassName
  41. * @param jobGroupName
  42. * @throws Exception
  43. */
  44. @PostMapping(value = "/pausejob")
  45. public void pausejob(@RequestParam(value = "jobClassName") String jobClassName,
  46. @RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
  47. qtzManager.pauseJob(jobClassName, jobGroupName);
  48. }
  49. /**
  50. * @param jobClassName
  51. * @param jobGroupName
  52. * @throws Exception
  53. */
  54. @PostMapping(value = "/resumejob")
  55. public void resumejob(@RequestParam(value = "jobClassName") String jobClassName,
  56. @RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
  57. qtzManager.resumeJob(jobClassName, jobGroupName);
  58. }
  59. /**
  60. * @param jobClassName
  61. * @param jobGroupName
  62. * @param cronExpression
  63. * @throws Exception
  64. */
  65. @PostMapping(value = "/reschedulejob")
  66. public void rescheduleJob(@RequestParam(value = "jobClassName") String jobClassName,
  67. @RequestParam(value = "jobGroupName") String jobGroupName,
  68. @RequestParam(value = "cronExpression") String cronExpression) throws Exception {
  69. qtzManager.addOrUpdateJob(getClass(jobClassName), jobClassName, jobGroupName, cronExpression);
  70. }
  71. /**
  72. * @param jobClassName
  73. * @param jobGroupName
  74. * @throws Exception
  75. */
  76. @PostMapping(value = "/deletejob")
  77. public void deletejob(@RequestParam(value = "jobClassName") String jobClassName,
  78. @RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
  79. qtzManager.deleteJob(jobClassName, jobGroupName);
  80. }
  81. /**
  82. * @param pageNum
  83. * @param pageSize
  84. * @return
  85. */
  86. @GetMapping(value = "/queryjob")
  87. public Map<String, Object> queryjob(@RequestParam(value = "pageNum") Integer pageNum,
  88. @RequestParam(value = "pageSize") Integer pageSize) {
  89. PageInfo<JobDetails> jobAndTrigger = qtzManager.queryAllJobBean(pageNum, pageSize);
  90. Map<String, Object> map = new HashMap<String, Object>();
  91. map.put("JobAndTrigger", jobAndTrigger);
  92. map.put("number", jobAndTrigger.getTotal());
  93. return map;
  94. }
  95. }
  • 定义具体的Job
  1. package tech.pdai.springboot.quartz.cluster.job;
  2. import java.util.Date;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.quartz.JobExecutionContext;
  5. import org.quartz.JobExecutionException;
  6. import org.springframework.scheduling.quartz.QuartzJobBean;
  7. @Slf4j
  8. public class HelloJob extends QuartzJobBean {
  9. @Override
  10. protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
  11. // get parameters
  12. context.getJobDetail().getJobDataMap().forEach(
  13. (k, v) -> log.info("param, key:{}, value:{}", k, v)
  14. );
  15. // your logics
  16. log.info("Hello Job执行时间: " + new Date());
  17. }
  18. }

前端实现

简单用VueJS 写个页面测试

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>QuartzDemo</title>
  6. <link rel="stylesheet" href="https://unpkg.com/element-ui@2.0.5/lib/theme-chalk/index.css">
  7. <script src="https://unpkg.com/vue/dist/vue.js"></script>
  8. <script src="http://cdn.bootcss.com/vue-resource/1.3.4/vue-resource.js"></script>
  9. <script src="https://unpkg.com/element-ui@2.0.5/lib/index.js"></script>
  10. <style>
  11. #top {
  12. background:#20A0FF;
  13. padding:5px;
  14. overflow:hidden
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="test">
  20. <div id="top">
  21. <el-button type="text" @click="search" style="color:white">查询</el-button>
  22. <el-button type="text" @click="handleadd" style="color:white">添加</el-button>
  23. </span>
  24. </div>
  25. <br/>
  26. <div style="margin-top:15px">
  27. <el-table
  28. ref="testTable"
  29. :data="tableData"
  30. style="width:100%"
  31. border
  32. >
  33. <el-table-column
  34. prop="status"
  35. label="任务状态"
  36. sortable
  37. show-overflow-tooltip>
  38. </el-table-column>
  39. <el-table-column
  40. prop="jobName"
  41. label="任务名称"
  42. sortable
  43. show-overflow-tooltip>
  44. </el-table-column>
  45. <el-table-column
  46. prop="jobGroupName"
  47. label="任务所在组"
  48. sortable>
  49. </el-table-column>
  50. <el-table-column
  51. prop="jobClassName"
  52. label="任务类名"
  53. sortable>
  54. </el-table-column>
  55. <el-table-column
  56. prop="triggerName"
  57. label="触发器名称"
  58. sortable>
  59. </el-table-column>
  60. <el-table-column
  61. prop="triggerGroupName"
  62. label="触发器所在组"
  63. sortable>
  64. </el-table-column>
  65. <el-table-column
  66. prop="cronExpression"
  67. label="表达式"
  68. sortable>
  69. </el-table-column>
  70. <el-table-column
  71. prop="timeZone"
  72. label="时区"
  73. sortable>
  74. </el-table-column>
  75. <el-table-column label="操作" width="300">
  76. <template scope="scope">
  77. <el-button
  78. size="small"
  79. type="warning"
  80. @click="handlePause(scope.$index, scope.row)">暂停</el-button>
  81. <el-button
  82. size="small"
  83. type="info"
  84. @click="handleResume(scope.$index, scope.row)">恢复</el-button>
  85. <el-button
  86. size="small"
  87. type="danger"
  88. @click="handleDelete(scope.$index, scope.row)">删除</el-button>
  89. <el-button
  90. size="small"
  91. type="success"
  92. @click="handleUpdate(scope.$index, scope.row)">修改</el-button>
  93. </template>
  94. </el-table-column>
  95. </el-table>
  96. <div align="center">
  97. <el-pagination
  98. @size-change="handleSizeChange"
  99. @current-change="handleCurrentChange"
  100. :current-page="currentPage"
  101. :page-sizes="[10, 20, 30, 40]"
  102. :page-size="pagesize"
  103. layout="total, sizes, prev, pager, next, jumper"
  104. :total="totalCount">
  105. </el-pagination>
  106. </div>
  107. </div>
  108. <el-dialog title="添加任务" :visible.sync="dialogFormVisible">
  109. <el-form :model="form">
  110. <el-form-item label="任务名称" label-width="120px" style="width:35%">
  111. <el-input v-model="form.jobName" auto-complete="off"></el-input>
  112. </el-form-item>
  113. <el-form-item label="任务分组" label-width="120px" style="width:35%">
  114. <el-input v-model="form.jobGroup" auto-complete="off"></el-input>
  115. </el-form-item>
  116. <el-form-item label="表达式" label-width="120px" style="width:35%">
  117. <el-input v-model="form.cronExpression" auto-complete="off"></el-input>
  118. </el-form-item>
  119. </el-form>
  120. <div slot="footer" class="dialog-footer">
  121. <el-button @click="dialogFormVisible = false">取 消</el-button>
  122. <el-button type="primary" @click="add">确 定</el-button>
  123. </div>
  124. </el-dialog>
  125. <el-dialog title="修改任务" :visible.sync="updateFormVisible">
  126. <el-form :model="updateform">
  127. <el-form-item label="表达式" label-width="120px" style="width:35%">
  128. <el-input v-model="updateform.cronExpression" auto-complete="off"></el-input>
  129. </el-form-item>
  130. </el-form>
  131. <div slot="footer" class="dialog-footer">
  132. <el-button @click="updateFormVisible = false">取 消</el-button>
  133. <el-button type="primary" @click="update">确 定</el-button>
  134. </div>
  135. </el-dialog>
  136. </div>
  137. <footer align="center">
  138. <p>&copy; Quartz 任务管理</p>
  139. </footer>
  140. <script>
  141. var vue = new Vue({
  142. el:"#test",
  143. data: {
  144. //表格当前页数据
  145. tableData: [],
  146. //请求的URL
  147. url:'job/queryjob',
  148. //默认每页数据量
  149. pagesize: 10,
  150. //当前页码
  151. currentPage: 1,
  152. //查询的页码
  153. start: 1,
  154. //默认数据总数
  155. totalCount: 1000,
  156. //添加对话框默认可见性
  157. dialogFormVisible: false,
  158. //修改对话框默认可见性
  159. updateFormVisible: false,
  160. //提交的表单
  161. form: {
  162. jobName: '',
  163. jobGroup: '',
  164. cronExpression: '',
  165. },
  166. updateform: {
  167. jobName: '',
  168. jobGroup: '',
  169. cronExpression: '',
  170. },
  171. },
  172. methods: {
  173. //从服务器读取数据
  174. loadData: function(pageNum, pageSize){
  175. this.$http.get('job/queryjob?' + 'pageNum=' + pageNum + '&pageSize=' + pageSize).then(function(res){
  176. console.log(res)
  177. this.tableData = res.body.JobAndTrigger.list;
  178. this.totalCount = res.body.number;
  179. },function(){
  180. console.log('failed');
  181. });
  182. },
  183. //单行删除
  184. handleDelete: function(index, row) {
  185. this.$http.post('job/deletejob',{"jobClassName":row.jobName,"jobGroupName":row.jobGroupName},{emulateJSON: true}).then(function(res){
  186. this.loadData( this.currentPage, this.pagesize);
  187. },function(){
  188. console.log('failed');
  189. });
  190. },
  191. //暂停任务
  192. handlePause: function(index, row){
  193. this.$http.post('job/pausejob',{"jobClassName":row.jobName,"jobGroupName":row.jobGroupName},{emulateJSON: true}).then(function(res){
  194. this.loadData( this.currentPage, this.pagesize);
  195. },function(){
  196. console.log('failed');
  197. });
  198. },
  199. //恢复任务
  200. handleResume: function(index, row){
  201. this.$http.post('job/resumejob',{"jobClassName":row.jobName,"jobGroupName":row.jobGroupName},{emulateJSON: true}).then(function(res){
  202. this.loadData( this.currentPage, this.pagesize);
  203. },function(){
  204. console.log('failed');
  205. });
  206. },
  207. //搜索
  208. search: function(){
  209. this.loadData(this.currentPage, this.pagesize);
  210. },
  211. //弹出对话框
  212. handleadd: function(){
  213. this.dialogFormVisible = true;
  214. },
  215. //添加
  216. add: function(){
  217. this.$http.post('job/addjob',{"jobClassName":this.form.jobName,"jobGroupName":this.form.jobGroup,"cronExpression":this.form.cronExpression},{emulateJSON: true}).then(function(res){
  218. this.loadData(this.currentPage, this.pagesize);
  219. this.dialogFormVisible = false;
  220. },function(){
  221. console.log('failed');
  222. });
  223. },
  224. //更新
  225. handleUpdate: function(index, row){
  226. console.log(row)
  227. this.updateFormVisible = true;
  228. this.updateform.jobName = row.jobClassName;
  229. this.updateform.jobGroup = row.jobGroupName;
  230. },
  231. //更新任务
  232. update: function(){
  233. this.$http.post
  234. ('job/reschedulejob',
  235. {"jobClassName":this.updateform.jobName,
  236. "jobGroupName":this.updateform.jobGroup,
  237. "cronExpression":this.updateform.cronExpression
  238. },{emulateJSON: true}
  239. ).then(function(res){
  240. this.loadData(this.currentPage, this.pagesize);
  241. this.updateFormVisible = false;
  242. },function(){
  243. console.log('failed');
  244. });
  245. },
  246. //每页显示数据量变更
  247. handleSizeChange: function(val) {
  248. this.pagesize = val;
  249. this.loadData(this.currentPage, this.pagesize);
  250. },
  251. //页码变更
  252. handleCurrentChange: function(val) {
  253. this.currentPage = val;
  254. this.loadData(this.currentPage, this.pagesize);
  255. },
  256. },
  257. });
  258. //载入数据
  259. vue.loadData(vue.currentPage, vue.pagesize);
  260. </script>
  261. </body>
  262. </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. 877. Stone Game - LeetCode

    Question 877. Stone Game Solution 题目大意: 说有偶数个数字,alex和lee两个人比赛,每次轮流从第一个数字或最后一个数字中拿走一个(偶数个数字,所以他俩拿的数字个 ...

  2. RealEvo-IDE安装

    双击"InstallWizard.exe"启动安装程序 点击"Install RealEvo-IDE"启动 RealEvo-IDE 安装程序 选择"下 ...

  3. 第06组Alpha冲刺总结

    目录 1. 基本情况 2. 思考与总结 2.1. 设想和目标 2. 计划 3. 资源 4. 变更管理 5. 设计/实现 6. 测试/发布 7. 团队的角色,管理,合作 8. 总结 3. 敏捷开发 1. ...

  4. 【Java】在IDEA中将Javafx项目打包成为可运行的.jar文件

    在使用Javafx制作一个图形化界面程序的时候,我遇到了打包文件的难题. 按照网上给出的解决方案构建出来的jar文件总是没有办法运行. 以下是我最终的解决方案. 我使用的IDE是IntelliJ ID ...

  5. st表 LCA

    我当时知道ST表可以 \(O(1)\) 求 LCA 的时候是极为震惊的,可以在需要反复使用 LCA 的时候卡常使用. ST表!用于解决 RMQ问题 ST表 我可能写得不好,看专业的 怎么实现? 考虑把 ...

  6. 测试平台系列(95) 前置条件支持简单的python脚本

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的教程,希望大家多多支持. 欢迎关注我的公众号米洛的测开日记,获取最新文章教程! 回顾 上一节我们构思了一下怎么去支 ...

  7. 使用C#和MonoGame开发俄罗斯方块游戏

    小的时候就看到有同学使用C语言在DOS下做过一款俄罗斯方块的游戏,当时是启用了DOS的图形化模式,感觉也挺有意思.最近上海疫情封控在家,周末也稍微有点空余时间,于是使用Visual Studio 20 ...

  8. SAP 定义用户组

    SUGR,可进行创建.查看.删除等维护性操作,并可指定本组的用户

  9. 上传几张.NET5之后的机器人logo

    上传几张.NET5之后的机器人logo

  10. 聊聊Netty那些事儿之从内核角度看IO模型

    从今天开始我们来聊聊Netty的那些事儿,我们都知道Netty是一个高性能异步事件驱动的网络框架. 它的设计异常优雅简洁,扩展性高,稳定性强.拥有非常详细完整的用户文档. 同时内置了很多非常有用的模块 ...