持久化配置

  1. spring:
  2. quartz:
  3. # quartz 相关属性配置
  4. properties:
  5. org:
  6. quartz:
  7. scheduler:
  8. instanceName: clusteredScheduler
  9. instanceId: AUTO
  10. jobStore:
  11. class: org.quartz.impl.jdbcjobstore.JobStoreTX
  12. driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  13. tablePrefix: QRTZ_
  14. isClustered: true
  15. clusterCheckinInterval: 10000
  16. useProperties: false
  17. threadPool:
  18. class: org.quartz.simpl.SimpleThreadPool
  19. threadCount: 10
  20. threadPriority: 5
  21. threadsInheritContextClassLoaderOfInitializingThread: true
  22. #数据库方式
  23. job-store-type: jdbc

配置说明

  1. #使用自己的配置文件
  2. org.quartz.jobStore.useProperties:true
  3. #默认或是自己改名字都行
  4. org.quartz.scheduler.instanceName: DefaultQuartzScheduler
  5. #如果使用集群,instanceId必须唯一,设置成AUTO
  6. org.quartz.scheduler.instanceId = AUTO
  7. org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
  8. org.quartz.threadPool.threadCount: 10
  9. org.quartz.threadPool.threadPriority: 5
  10. org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
  11. #存储方式使用JobStoreTX,也就是数据库
  12. org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
  13. org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  14. #是否使用集群(如果项目只部署到 一台服务器,就不用了)
  15. org.quartz.jobStore.isClustered = true
  16. org.quartz.jobStore.clusterCheckinInterval=20000
  17. org.quartz.jobStore.tablePrefix = QRTZ_
  18. org.quartz.jobStore.dataSource = myDS
  19. #配置数据源
  20. #数据库中quartz表的表名前缀
  21. org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
  22. org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8
  23. org.quartz.dataSource.myDS.user = root
  24. org.quartz.dataSource.myDS.password = 123456
  25. org.quartz.dataSource.myDS.maxConnections = 5

数据库设置

  1. #数据库引擎
  2. org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
  3. #数据库连接
  4. org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
  5. #数据库用户
  6. org.quartz.dataSource.myDS.user = root
  7. #数据库密码
  8. org.quartz.dataSource.myDS.password = 123456
  9. #允许最大连接
  10. org.quartz.dataSource.myDS.maxConnections = 5
  11. #验证查询sql,可以不设置
  12. org.quartz.dataSource.myDS.validationQuery=select 0 from dual

Mysql 表

数据库地址 https://github.com/quartz-scheduler/quartz/blob/master/quartz-core/src/main/resources/org/quartz/impl/jdbcjobstore/tables_mysql_innodb.sql
quartz_innodb.sql

  1. -- in your Quartz properties file, you'll need to set org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  2. -- 你需要在你的quartz.properties文件中设置org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  3. -- StdJDBCDelegate说明支持集群,所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务
  4. -- This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM
  5. -- 这是来自quartz的脚本,在MySQL数据库中创建以下的表,修改为使用INNODB而不是MYISAM
  6. -- 你需要在数据库中执行以下的sql脚本
  7. DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
  8. DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
  9. DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
  10. DROP TABLE IF EXISTS QRTZ_LOCKS;
  11. DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
  12. DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
  13. DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
  14. DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
  15. DROP TABLE IF EXISTS QRTZ_TRIGGERS;
  16. DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
  17. DROP TABLE IF EXISTS QRTZ_CALENDARS;
  18. -- 存储每一个已配置的Job的详细信息
  19. CREATE TABLE QRTZ_JOB_DETAILS(
  20. SCHED_NAME VARCHAR(120) NOT NULL,
  21. JOB_NAME VARCHAR(200) NOT NULL,
  22. JOB_GROUP VARCHAR(200) NOT NULL,
  23. DESCRIPTION VARCHAR(250) NULL,
  24. JOB_CLASS_NAME VARCHAR(250) NOT NULL,
  25. IS_DURABLE VARCHAR(1) NOT NULL,
  26. IS_NONCONCURRENT VARCHAR(1) NOT NULL,
  27. IS_UPDATE_DATA VARCHAR(1) NOT NULL,
  28. REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
  29. JOB_DATA BLOB NULL,
  30. PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
  31. ENGINE=InnoDB;
  32. -- 存储已配置的Trigger的信息
  33. CREATE TABLE QRTZ_TRIGGERS (
  34. SCHED_NAME VARCHAR(120) NOT NULL,
  35. TRIGGER_NAME VARCHAR(200) NOT NULL,
  36. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  37. JOB_NAME VARCHAR(200) NOT NULL,
  38. JOB_GROUP VARCHAR(200) NOT NULL,
  39. DESCRIPTION VARCHAR(250) NULL,
  40. NEXT_FIRE_TIME BIGINT(13) NULL,
  41. PREV_FIRE_TIME BIGINT(13) NULL,
  42. PRIORITY INTEGER NULL,
  43. TRIGGER_STATE VARCHAR(16) NOT NULL,
  44. TRIGGER_TYPE VARCHAR(8) NOT NULL,
  45. START_TIME BIGINT(13) NOT NULL,
  46. END_TIME BIGINT(13) NULL,
  47. CALENDAR_NAME VARCHAR(200) NULL,
  48. MISFIRE_INSTR SMALLINT(2) NULL,
  49. JOB_DATA BLOB NULL,
  50. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  51. FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
  52. REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
  53. ENGINE=InnoDB;
  54. -- 存储已配置的Simple Trigger的信息
  55. CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
  56. SCHED_NAME VARCHAR(120) NOT NULL,
  57. TRIGGER_NAME VARCHAR(200) NOT NULL,
  58. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  59. REPEAT_COUNT BIGINT(7) NOT NULL,
  60. REPEAT_INTERVAL BIGINT(12) NOT NULL,
  61. TIMES_TRIGGERED BIGINT(10) NOT NULL,
  62. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  63. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  64. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  65. ENGINE=InnoDB;
  66. -- 存储Cron Trigger,包括Cron表达式和时区信息
  67. CREATE TABLE QRTZ_CRON_TRIGGERS (
  68. SCHED_NAME VARCHAR(120) NOT NULL,
  69. TRIGGER_NAME VARCHAR(200) NOT NULL,
  70. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  71. CRON_EXPRESSION VARCHAR(120) NOT NULL,
  72. TIME_ZONE_ID VARCHAR(80),
  73. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  74. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  75. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  76. ENGINE=InnoDB;
  77. CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  78. (
  79. SCHED_NAME VARCHAR(120) NOT NULL,
  80. TRIGGER_NAME VARCHAR(200) NOT NULL,
  81. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  82. STR_PROP_1 VARCHAR(512) NULL,
  83. STR_PROP_2 VARCHAR(512) NULL,
  84. STR_PROP_3 VARCHAR(512) NULL,
  85. INT_PROP_1 INT NULL,
  86. INT_PROP_2 INT NULL,
  87. LONG_PROP_1 BIGINT NULL,
  88. LONG_PROP_2 BIGINT NULL,
  89. DEC_PROP_1 NUMERIC(13,4) NULL,
  90. DEC_PROP_2 NUMERIC(13,4) NULL,
  91. BOOL_PROP_1 VARCHAR(1) NULL,
  92. BOOL_PROP_2 VARCHAR(1) NULL,
  93. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  94. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  95. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  96. ENGINE=InnoDB;
  97. -- Trigger作为Blob类型存储(用于Quartz用户用JDBC创建他们自己定制的Trigger类型,JobStore并不知道如何存储实例的时候)
  98. CREATE TABLE QRTZ_BLOB_TRIGGERS (
  99. SCHED_NAME VARCHAR(120) NOT NULL,
  100. TRIGGER_NAME VARCHAR(200) NOT NULL,
  101. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  102. BLOB_DATA BLOB NULL,
  103. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  104. INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
  105. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  106. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  107. ENGINE=InnoDB;
  108. -- 以Blob类型存储Quartz的Calendar日历信息,quartz可配置一个日历来指定一个时间范围
  109. CREATE TABLE QRTZ_CALENDARS (
  110. SCHED_NAME VARCHAR(120) NOT NULL,
  111. CALENDAR_NAME VARCHAR(200) NOT NULL,
  112. CALENDAR BLOB NOT NULL,
  113. PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
  114. ENGINE=InnoDB;
  115. -- 存储已暂停的Trigger组的信息
  116. CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
  117. SCHED_NAME VARCHAR(120) NOT NULL,
  118. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  119. PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
  120. ENGINE=InnoDB;
  121. -- 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息
  122. CREATE TABLE QRTZ_FIRED_TRIGGERS (
  123. SCHED_NAME VARCHAR(120) NOT NULL,
  124. ENTRY_ID VARCHAR(95) NOT NULL,
  125. TRIGGER_NAME VARCHAR(200) NOT NULL,
  126. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  127. INSTANCE_NAME VARCHAR(200) NOT NULL,
  128. FIRED_TIME BIGINT(13) NOT NULL,
  129. SCHED_TIME BIGINT(13) NOT NULL,
  130. PRIORITY INTEGER NOT NULL,
  131. STATE VARCHAR(16) NOT NULL,
  132. JOB_NAME VARCHAR(200) NULL,
  133. JOB_GROUP VARCHAR(200) NULL,
  134. IS_NONCONCURRENT VARCHAR(1) NULL,
  135. REQUESTS_RECOVERY VARCHAR(1) NULL,
  136. PRIMARY KEY (SCHED_NAME,ENTRY_ID))
  137. ENGINE=InnoDB;
  138. -- 存储少量的有关 Scheduler的状态信息,和别的 Scheduler 实例(假如是用于一个集群中)
  139. CREATE TABLE QRTZ_SCHEDULER_STATE (
  140. SCHED_NAME VARCHAR(120) NOT NULL,
  141. INSTANCE_NAME VARCHAR(200) NOT NULL,
  142. LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
  143. CHECKIN_INTERVAL BIGINT(13) NOT NULL,
  144. PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
  145. ENGINE=InnoDB;
  146. -- 存储程序的非观锁的信息(假如使用了悲观锁)
  147. CREATE TABLE QRTZ_LOCKS (
  148. SCHED_NAME VARCHAR(120) NOT NULL,
  149. LOCK_NAME VARCHAR(40) NOT NULL,
  150. PRIMARY KEY (SCHED_NAME,LOCK_NAME))
  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. commit;

表定义

  • qrtz_job_details:记录每个任务的详细信息。
  • qrtz_triggers:记录每个触发器的详细信息。
  • qrtz_corn_triggers:记录cornTrigger的信息。
  • qrtz_scheduler_state:记录 调度器(每个机器节点)的生命状态。
  • qrtz_fired_triggers:记录每个正在执行的触发器。
  • qrtz_locks:记录程序的悲观锁(防止多个节点同时执行同一个定时任务)

qrtz_job_details

  1. CREATE TABLE `qrtz_job_details` (
  2. `SCHED_NAME` varchar(120) COLLATE utf8_bin NOT NULL COMMENT '调度器名,集群环境中使用,必须使用同一个名称——集群环境下”逻辑”相同的scheduler,默认为QuartzScheduler',
  3. `JOB_NAME` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '集群中job的名字',
  4. `JOB_GROUP` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '集群中job的所属组的名字',
  5. `DESCRIPTION` varchar(250) COLLATE utf8_bin DEFAULT NULL COMMENT '描述',
  6. `JOB_CLASS_NAME` varchar(250) COLLATE utf8_bin NOT NULL COMMENT '集群中个note job实现类的完全包名,quartz就是根据这个路径到classpath找到该job类',
  7. `IS_DURABLE` varchar(1) COLLATE utf8_bin NOT NULL COMMENT '是否持久化,把该属性设置为1,quartz会把job持久化到数据库中',
  8. `IS_NONCONCURRENT` varchar(1) COLLATE utf8_bin NOT NULL COMMENT '是否并行,该属性可以通过注解配置',
  9. `IS_UPDATE_DATA` varchar(1) COLLATE utf8_bin NOT NULL,
  10. `REQUESTS_RECOVERY` varchar(1) COLLATE utf8_bin NOT NULL COMMENT '当一个scheduler失败后,其他实例可以发现那些执行失败的Jobs,若是1,那么该Job会被其他实例重新执行,否则对应的Job只能释放等待下次触发',
  11. `JOB_DATA` blob COMMENT '一个blob字段,存放持久化job对象',
  12. PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`)
  13. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='存储每一个已配置的 Job 的详细信息';

Job 的状态

如果我们有个需求是统计每个任务的执行次数,那么你会怎么做?

也许你会想到使用上面说到的 JobDataMap,那就让我们尝试下:

  • 任务调度类

    1. // 我们在 JobDataMap 中定义了一个值为 0 的初始值
    2. JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
    3. .usingJobData("executeCount", 0)
    4. .withIdentity("testJob", "testJobGroup")
    5. .build();
  • Job 任务类

    1. @Slf4j
    2. public class TestJob implements Job {
    3. private Integer executeCount;
    4. public void setExecuteCount(Integer executeCount) {
    5. this.executeCount = executeCount;
    6. }
    7. @Override
    8. public void execute(JobExecutionContext jobExecutionContext) {
    9. String data = LocalDateTime.now()
    10. .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    11. log.info("execute count: {}, current time: {}",
    12. ++executeCount, data);
    13. //将累加的 count 存入JobDataMap中
    14. jobExecutionContext.getJobDetail()
    15. .getJobDataMap().put("executeCount", executeCount);
    16. }
    17. }
    18. /** OUTPUT:
    19. execute count: 1, current time: 2020-11-17 22:38:48
    20. execute count: 1, current time: 2020-11-17 22:38:52
    21. execute count: 1, current time: 2020-11-17 22:38:57
    22. **/

    按照上面的想法我们写出了这部分代码,但貌似打脸了,结果并没有按照我们预计的发展,是逻辑不对吗,貌似写的也没什么问题。这时你会不会回忆到上面我讲过的一句话:“在调用 execute 方法之前都会创建一个新的 Job 实例”,这就牵引出了 Job 状态的概念:

  • 无状态的 Job

每次调用时都会创建一个新的 JobDataMap

  • 有状态的 Job

多次 Job 调用可以持有一些状态信息,这些状态信息存储在 JobDataMap
那么问题来了,如果让 Job 变成有状态?这个时候我们可以借助一个注解:@PersistJobDataAfterExecution ,加上这个注解后,我们再来试下:
Job 任务类:

  1. @Slf4j
  2. @PersistJobDataAfterExecution
  3. public class TestJob implements Job {
  4. private Integer executeCount;
  5. public void setExecuteCount(Integer executeCount) {
  6. this.executeCount = executeCount;
  7. }
  8. @Override
  9. public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
  10. String data = LocalDateTime.now().
  11. format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
  12. log.info("execute count: {}, current time: {}",
  13. ++executeCount, data);
  14. //将累加的 count 存入JobDataMap中
  15. jobExecutionContext.getJobDetail().
  16. getJobDataMap().put("executeCount", executeCount);
  17. }
  18. }
  19. /** OUTPUT:
  20. execute count: 1, current time: 2020-11-17 22:28:48
  21. execute count: 2, current time: 2020-11-17 22:28:52
  22. execute count: 3, current time: 2020-11-17 22:28:57
  23. **/

可以看到加了 @PersistJobDataAfterExecution ,我们已经成功达到了我们的目的。

参考

https://www.jianshu.com/p/b94ebb8780fa
https://ld246.com/article/1561629454128
https://segmentfault.com/a/1190000016554033