当前位置:   article > 正文

SpringBoot整合Quartz 实现分布式定时任务调度_quartz autostartup

quartz autostartup

一、Quartz 集群架构

Quartz 是 Java 领域最著名的开源任务调度工具。

在上篇文章中,我们详细的介绍了 Quartz 的单体应用实践,如果只在单体环境中应用,Quartz 未必是最好的选择,例如Spring Scheduled一样也可以实现任务调度,并且与SpringBoot无缝集成,支持注解配置,非常简单,但是它有个缺点就是在集群环境下,会导致任务被重复调度!

而与之对应的 Quartz 提供了极为广用的特性,如任务持久化、集群部署和分布式调度任务等等,正因如此,基于 Quartz 任务调度功能在系统开发中应用极为广泛!

在集群环境下,Quartz 集群中的每个节点是一个独立的 Quartz 应用,没有负责集中管理的节点,而是通过数据库表来感知另一个应用,利用数据库锁的方式来实现集群环境下进行并发控制,每个任务当前运行的有效节点有且只有一个!

特别需要注意的是:分布式部署时需要保证各个节点的系统时间一致!

 二、数据表初始化

数据库表结构官网已经提供,我们可以直接访问Quartz对应的官方网站,找到对应的版本,然后将其下载!

 我选择的是quartz-2.3.0-distribution.tar.gz,下载完成之后将其解压,在文件中搜索sql,在里面选择适合当前环境的数据库脚本文件,然后将其初始化到数据库中即可!

例如,我使用的数据库是mysql-5.7,因此我选择的是tables_mysql_innodb.sql脚本,具体内容如下: 

  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. CREATE TABLE QRTZ_JOB_DETAILS(
  13. SCHED_NAME VARCHAR(120) NOT NULL,
  14. JOB_NAME VARCHAR(190) NOT NULL,
  15. JOB_GROUP VARCHAR(190) NOT NULL,
  16. DESCRIPTION VARCHAR(250) NULL,
  17. JOB_CLASS_NAME VARCHAR(250) NOT NULL,
  18. IS_DURABLE VARCHAR(1) NOT NULL,
  19. IS_NONCONCURRENT VARCHAR(1) NOT NULL,
  20. IS_UPDATE_DATA VARCHAR(1) NOT NULL,
  21. REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
  22. JOB_DATA BLOB NULL,
  23. PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
  24. ENGINE=InnoDB;
  25. CREATE TABLE QRTZ_TRIGGERS (
  26. SCHED_NAME VARCHAR(120) NOT NULL,
  27. TRIGGER_NAME VARCHAR(190) NOT NULL,
  28. TRIGGER_GROUP VARCHAR(190) NOT NULL,
  29. JOB_NAME VARCHAR(190) NOT NULL,
  30. JOB_GROUP VARCHAR(190) NOT NULL,
  31. DESCRIPTION VARCHAR(250) NULL,
  32. NEXT_FIRE_TIME BIGINT(13) NULL,
  33. PREV_FIRE_TIME BIGINT(13) NULL,
  34. PRIORITY INTEGER NULL,
  35. TRIGGER_STATE VARCHAR(16) NOT NULL,
  36. TRIGGER_TYPE VARCHAR(8) NOT NULL,
  37. START_TIME BIGINT(13) NOT NULL,
  38. END_TIME BIGINT(13) NULL,
  39. CALENDAR_NAME VARCHAR(190) NULL,
  40. MISFIRE_INSTR SMALLINT(2) NULL,
  41. JOB_DATA BLOB NULL,
  42. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  43. FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
  44. REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
  45. ENGINE=InnoDB;
  46. CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
  47. SCHED_NAME VARCHAR(120) NOT NULL,
  48. TRIGGER_NAME VARCHAR(190) NOT NULL,
  49. TRIGGER_GROUP VARCHAR(190) NOT NULL,
  50. REPEAT_COUNT BIGINT(7) NOT NULL,
  51. REPEAT_INTERVAL BIGINT(12) NOT NULL,
  52. TIMES_TRIGGERED BIGINT(10) NOT NULL,
  53. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  54. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  55. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  56. ENGINE=InnoDB;
  57. CREATE TABLE QRTZ_CRON_TRIGGERS (
  58. SCHED_NAME VARCHAR(120) NOT NULL,
  59. TRIGGER_NAME VARCHAR(190) NOT NULL,
  60. TRIGGER_GROUP VARCHAR(190) NOT NULL,
  61. CRON_EXPRESSION VARCHAR(120) NOT NULL,
  62. TIME_ZONE_ID VARCHAR(80),
  63. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  64. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  65. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  66. ENGINE=InnoDB;
  67. CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  68. (
  69. SCHED_NAME VARCHAR(120) NOT NULL,
  70. TRIGGER_NAME VARCHAR(190) NOT NULL,
  71. TRIGGER_GROUP VARCHAR(190) NOT NULL,
  72. STR_PROP_1 VARCHAR(512) NULL,
  73. STR_PROP_2 VARCHAR(512) NULL,
  74. STR_PROP_3 VARCHAR(512) NULL,
  75. INT_PROP_1 INT NULL,
  76. INT_PROP_2 INT NULL,
  77. LONG_PROP_1 BIGINT NULL,
  78. LONG_PROP_2 BIGINT NULL,
  79. DEC_PROP_1 NUMERIC(13,4) NULL,
  80. DEC_PROP_2 NUMERIC(13,4) NULL,
  81. BOOL_PROP_1 VARCHAR(1) NULL,
  82. BOOL_PROP_2 VARCHAR(1) NULL,
  83. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  84. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  85. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
  86. ENGINE=InnoDB;
  87. CREATE TABLE QRTZ_BLOB_TRIGGERS (
  88. SCHED_NAME VARCHAR(120) NOT NULL,
  89. TRIGGER_NAME VARCHAR(190) NOT NULL,
  90. TRIGGER_GROUP VARCHAR(190) NOT NULL,
  91. BLOB_DATA BLOB NULL,
  92. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  93. INDEX (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. CREATE TABLE QRTZ_CALENDARS (
  98. SCHED_NAME VARCHAR(120) NOT NULL,
  99. CALENDAR_NAME VARCHAR(190) NOT NULL,
  100. CALENDAR BLOB NOT NULL,
  101. PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
  102. ENGINE=InnoDB;
  103. CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
  104. SCHED_NAME VARCHAR(120) NOT NULL,
  105. TRIGGER_GROUP VARCHAR(190) NOT NULL,
  106. PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
  107. ENGINE=InnoDB;
  108. CREATE TABLE QRTZ_FIRED_TRIGGERS (
  109. SCHED_NAME VARCHAR(120) NOT NULL,
  110. ENTRY_ID VARCHAR(95) NOT NULL,
  111. TRIGGER_NAME VARCHAR(190) NOT NULL,
  112. TRIGGER_GROUP VARCHAR(190) NOT NULL,
  113. INSTANCE_NAME VARCHAR(190) NOT NULL,
  114. FIRED_TIME BIGINT(13) NOT NULL,
  115. SCHED_TIME BIGINT(13) NOT NULL,
  116. PRIORITY INTEGER NOT NULL,
  117. STATE VARCHAR(16) NOT NULL,
  118. JOB_NAME VARCHAR(190) NULL,
  119. JOB_GROUP VARCHAR(190) NULL,
  120. IS_NONCONCURRENT VARCHAR(1) NULL,
  121. REQUESTS_RECOVERY VARCHAR(1) NULL,
  122. PRIMARY KEY (SCHED_NAME,ENTRY_ID))
  123. ENGINE=InnoDB;
  124. CREATE TABLE QRTZ_SCHEDULER_STATE (
  125. SCHED_NAME VARCHAR(120) NOT NULL,
  126. INSTANCE_NAME VARCHAR(190) NOT NULL,
  127. LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
  128. CHECKIN_INTERVAL BIGINT(13) NOT NULL,
  129. PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
  130. ENGINE=InnoDB;
  131. CREATE TABLE QRTZ_LOCKS (
  132. SCHED_NAME VARCHAR(120) NOT NULL,
  133. LOCK_NAME VARCHAR(40) NOT NULL,
  134. PRIMARY KEY (SCHED_NAME,LOCK_NAME))
  135. ENGINE=InnoDB;
  136. CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
  137. CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
  138. CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
  139. CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
  140. CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
  141. CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
  142. CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
  143. CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
  144. CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
  145. CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
  146. CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
  147. CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
  148. CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
  149. CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
  150. CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
  151. CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
  152. CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
  153. CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
  154. CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
  155. CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
  156. commit;

具体表描述如下:

 其中,QRTZ_LOCKS 就是 Quartz 集群实现同步机制的行锁表!

三、Quartz 集群实践

3.1、创建springboot项目,导入maven依赖包

  1. <!--引入boot父类-->
  2. <parent>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-parent</artifactId>
  5. <version>2.1.0.RELEASE</version>
  6. </parent>
  7. <!--引入相关包-->
  8. <dependencies>
  9. <!--spring boot核心-->
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter</artifactId>
  13. </dependency>
  14. <!--spring boot 测试-->
  15. <dependency>
  16. <groupId>org.springframework.boot</groupId>
  17. <artifactId>spring-boot-starter-test</artifactId>
  18. <scope>test</scope>
  19. </dependency>
  20. <!--springmvc web-->
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-web</artifactId>
  24. </dependency>
  25. <!--开发环境调试-->
  26. <dependency>
  27. <groupId>org.springframework.boot</groupId>
  28. <artifactId>spring-boot-devtools</artifactId>
  29. <optional>true</optional>
  30. </dependency>
  31. <!--jpa 支持-->
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-data-jpa</artifactId>
  35. </dependency>
  36. <!--mysql 数据源-->
  37. <dependency>
  38. <groupId>mysql</groupId>
  39. <artifactId>mysql-connector-java</artifactId>
  40. </dependency>
  41. <!--druid 数据连接池-->
  42. <dependency>
  43. <groupId>com.alibaba</groupId>
  44. <artifactId>druid-spring-boot-starter</artifactId>
  45. <version>1.1.17</version>
  46. </dependency>
  47. <dependency>
  48. <groupId>org.springframework.boot</groupId>
  49. <artifactId>spring-boot-starter-quartz</artifactId>
  50. </dependency>
  51. <!--Alibaba Json处理包 -->
  52. <dependency>
  53. <groupId>com.alibaba</groupId>
  54. <artifactId>fastjson</artifactId>
  55. <version>1.2.46</version>
  56. </dependency>
  57. </dependencies>

3.2、创建 application.properties 配置文件

  1. spring.application.name=springboot-quartz-001
  2. server.port=8080
  3. #引入数据源
  4. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
  5. spring.datasource.username=root
  6. spring.datasource.password=123456
  7. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

3.3、创建 quartz.properties 配置文件

  1. #调度配置
  2. #调度器实例名称
  3. org.quartz.scheduler.instanceName=SsmScheduler
  4. #调度器实例编号自动生成
  5. org.quartz.scheduler.instanceId=AUTO
  6. #是否在Quartz执行一个job前使用UserTransaction
  7. org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
  8. #线程池配置
  9. #线程池的实现类
  10. org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
  11. #线程池中的线程数量
  12. org.quartz.threadPool.threadCount=10
  13. #线程优先级
  14. org.quartz.threadPool.threadPriority=5
  15. #配置是否启动自动加载数据库内的定时任务,默认true
  16. org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
  17. #是否设置为守护线程,设置后任务将不会执行
  18. #org.quartz.threadPool.makeThreadsDaemons=true
  19. #持久化方式配置
  20. #JobDataMaps是否都为String类型
  21. org.quartz.jobStore.useProperties=true
  22. #数据表的前缀,默认QRTZ_
  23. org.quartz.jobStore.tablePrefix=QRTZ_
  24. #最大能忍受的触发超时时间
  25. org.quartz.jobStore.misfireThreshold=60000
  26. #是否以集群方式运行
  27. org.quartz.jobStore.isClustered=true
  28. #调度实例失效的检查时间间隔,单位毫秒
  29. org.quartz.jobStore.clusterCheckinInterval=2000
  30. #数据保存方式为数据库持久化
  31. org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
  32. #数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库
  33. org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  34. #数据库别名 随便取
  35. org.quartz.jobStore.dataSource=qzDS
  36. #数据库连接池,将其设置为druid
  37. org.quartz.dataSource.qzDS.connectionProvider.class=com.example.cluster.quartz.config.DruidConnectionProvider
  38. #数据库引擎
  39. org.quartz.dataSource.qzDS.driver=com.mysql.cj.jdbc.Driver
  40. #数据库连接
  41. org.quartz.dataSource.qzDS.URL=jdbc:mysql://127.0.0.1:3306/test-quartz?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
  42. #数据库用户
  43. org.quartz.dataSource.qzDS.user=root
  44. #数据库密码
  45. org.quartz.dataSource.qzDS.password=123456
  46. #允许最大连接
  47. org.quartz.dataSource.qzDS.maxConnection=5
  48. #验证查询sql,可以不设置
  49. org.quartz.dataSource.qzDS.validationQuery=select 0 from dual

3.4、注册 Quartz 任务工厂

  1. @Component
  2. public class QuartzJobFactory extends AdaptableJobFactory {
  3. @Autowired
  4. private AutowireCapableBeanFactory capableBeanFactory;
  5. @Override
  6. protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
  7. //调用父类的方法
  8. Object jobInstance = super.createJobInstance(bundle);
  9. //进行注入
  10. capableBeanFactory.autowireBean(jobInstance);
  11. return jobInstance;
  12. }
  13. }

3.5、注册调度工厂

  1. @Configuration
  2. public class QuartzConfig {
  3. @Autowired
  4. private QuartzJobFactory jobFactory;
  5. @Bean
  6. public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
  7. //获取配置属性
  8. PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
  9. propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
  10. //在quartz.properties中的属性被读取并注入后再初始化对象
  11. propertiesFactoryBean.afterPropertiesSet();
  12. //创建SchedulerFactoryBean
  13. SchedulerFactoryBean factory = new SchedulerFactoryBean();
  14. factory.setQuartzProperties(propertiesFactoryBean.getObject());
  15. factory.setJobFactory(jobFactory);//支持在JOB实例中注入其他的业务对象
  16. factory.setApplicationContextSchedulerContextKey("applicationContextKey");
  17. factory.setWaitForJobsToCompleteOnShutdown(true);//这样当spring关闭时,会等待所有已经启动的quartz job结束后spring才能完全shutdown。
  18. factory.setOverwriteExistingJobs(false);//是否覆盖己存在的Job
  19. factory.setStartupDelay(10);//QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动
  20. return factory;
  21. }
  22. /**
  23. * 通过SchedulerFactoryBean获取Scheduler的实例
  24. * @return
  25. * @throws IOException
  26. * @throws SchedulerException
  27. */
  28. @Bean(name = "scheduler")
  29. public Scheduler scheduler() throws IOException, SchedulerException {
  30. Scheduler scheduler = schedulerFactoryBean().getScheduler();
  31. return scheduler;
  32. }
  33. }

3.6、重新设置 Quartz 数据连接池

默认 Quartz 的数据连接池是 c3p0,由于性能不太稳定,不推荐使用,因此我们将其改成driud数据连接池,配置如下:

  1. public class DruidConnectionProvider implements ConnectionProvider {
  2. /**
  3. * 常量配置,与quartz.properties文件的key保持一致(去掉前缀),同时提供set方法,Quartz框架自动注入值。
  4. * @return
  5. * @throws SQLException
  6. */
  7. //JDBC驱动
  8. public String driver;
  9. //JDBC连接串
  10. public String URL;
  11. //数据库用户名
  12. public String user;
  13. //数据库用户密码
  14. public String password;
  15. //数据库最大连接数
  16. public int maxConnection;
  17. //数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。
  18. public String validationQuery;
  19. private boolean validateOnCheckout;
  20. private int idleConnectionValidationSeconds;
  21. public String maxCachedStatementsPerConnection;
  22. private String discardIdleConnectionsSeconds;
  23. public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;
  24. public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;
  25. //Druid连接池
  26. private DruidDataSource datasource;
  27. @Override
  28. public Connection getConnection() throws SQLException {
  29. return datasource.getConnection();
  30. }
  31. @Override
  32. public void shutdown() throws SQLException {
  33. datasource.close();
  34. }
  35. @Override
  36. public void initialize() throws SQLException {
  37. if (this.URL == null) {
  38. throw new SQLException("DBPool could not be created: DB URL cannot be null");
  39. }
  40. if (this.driver == null) {
  41. throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!");
  42. }
  43. if (this.maxConnection < 0) {
  44. throw new SQLException("DBPool maxConnectins could not be created: Max connections must be greater than zero!");
  45. }
  46. datasource = new DruidDataSource();
  47. try{
  48. datasource.setDriverClassName(this.driver);
  49. } catch (Exception e) {
  50. try {
  51. throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e);
  52. } catch (SchedulerException e1) {
  53. }
  54. }
  55. datasource.setUrl(this.URL);
  56. datasource.setUsername(this.user);
  57. datasource.setPassword(this.password);
  58. datasource.setMaxActive(this.maxConnection);
  59. datasource.setMinIdle(1);
  60. datasource.setMaxWait(0);
  61. datasource.setMaxPoolPreparedStatementPerConnectionSize(DEFAULT_DB_MAX_CONNECTIONS);
  62. if (this.validationQuery != null) {
  63. datasource.setValidationQuery(this.validationQuery);
  64. if(!this.validateOnCheckout)
  65. datasource.setTestOnReturn(true);
  66. else
  67. datasource.setTestOnBorrow(true);
  68. datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);
  69. }
  70. }
  71. public String getDriver() {
  72. return driver;
  73. }
  74. public void setDriver(String driver) {
  75. this.driver = driver;
  76. }
  77. public String getURL() {
  78. return URL;
  79. }
  80. public void setURL(String URL) {
  81. this.URL = URL;
  82. }
  83. public String getUser() {
  84. return user;
  85. }
  86. public void setUser(String user) {
  87. this.user = user;
  88. }
  89. public String getPassword() {
  90. return password;
  91. }
  92. public void setPassword(String password) {
  93. this.password = password;
  94. }
  95. public int getMaxConnection() {
  96. return maxConnection;
  97. }
  98. public void setMaxConnection(int maxConnection) {
  99. this.maxConnection = maxConnection;
  100. }
  101. public String getValidationQuery() {
  102. return validationQuery;
  103. }
  104. public void setValidationQuery(String validationQuery) {
  105. this.validationQuery = validationQuery;
  106. }
  107. public boolean isValidateOnCheckout() {
  108. return validateOnCheckout;
  109. }
  110. public void setValidateOnCheckout(boolean validateOnCheckout) {
  111. this.validateOnCheckout = validateOnCheckout;
  112. }
  113. public int getIdleConnectionValidationSeconds() {
  114. return idleConnectionValidationSeconds;
  115. }
  116. public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {
  117. this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;
  118. }
  119. public DruidDataSource getDatasource() {
  120. return datasource;
  121. }
  122. public void setDatasource(DruidDataSource datasource) {
  123. this.datasource = datasource;
  124. }
  125. public String getDiscardIdleConnectionsSeconds() {
  126. return discardIdleConnectionsSeconds;
  127. }
  128. public void setDiscardIdleConnectionsSeconds(String discardIdleConnectionsSeconds) {
  129. this.discardIdleConnectionsSeconds = discardIdleConnectionsSeconds;
  130. }
  131. }

创建完成之后,还需要在quartz.properties配置文件中设置一下即可!

  1. #数据库连接池,将其设置为druid
  2. org.quartz.dataSource.qzDS.connectionProvider.class=com.example.cluster.quartz.config.DruidConnectionProvider

如果已经配置,请忽略!

3.7、编写 Job 具体任务类

  1. public class TfCommandJob implements Job {
  2. private static final Logger log = LoggerFactory.getLogger(TfCommandJob.class);
  3. @Override
  4. public void execute(JobExecutionContext context) {
  5. try {
  6. System.out.println(context.getScheduler().getSchedulerInstanceId() + "--" + new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date()));
  7. } catch (SchedulerException e) {
  8. log.error("任务执行失败",e);
  9. }
  10. }
  11. }

3.8、编写 Quartz 服务层接口

  1. public interface QuartzJobService {
  2. /**
  3. * 添加任务可以传参数
  4. * @param clazzName
  5. * @param jobName
  6. * @param groupName
  7. * @param cronExp
  8. * @param param
  9. */
  10. void addJob(String clazzName, String jobName, String groupName, String cronExp, Map<String, Object> param);
  11. /**
  12. * 暂停任务
  13. * @param jobName
  14. * @param groupName
  15. */
  16. void pauseJob(String jobName, String groupName);
  17. /**
  18. * 恢复任务
  19. * @param jobName
  20. * @param groupName
  21. */
  22. void resumeJob(String jobName, String groupName);
  23. /**
  24. * 立即运行一次定时任务
  25. * @param jobName
  26. * @param groupName
  27. */
  28. void runOnce(String jobName, String groupName);
  29. /**
  30. * 更新任务
  31. * @param jobName
  32. * @param groupName
  33. * @param cronExp
  34. * @param param
  35. */
  36. void updateJob(String jobName, String groupName, String cronExp, Map<String, Object> param);
  37. /**
  38. * 删除任务
  39. * @param jobName
  40. * @param groupName
  41. */
  42. void deleteJob(String jobName, String groupName);
  43. /**
  44. * 启动所有任务
  45. */
  46. void startAllJobs();
  47. /**
  48. * 暂停所有任务
  49. */
  50. void pauseAllJobs();
  51. /**
  52. * 恢复所有任务
  53. */
  54. void resumeAllJobs();
  55. /**
  56. * 关闭所有任务
  57. */
  58. void shutdownAllJobs();
  59. }

对应的实现类QuartzJobServiceImpl如下:

  1. @Service
  2. public class QuartzJobServiceImpl implements QuartzJobService {
  3. private static final Logger log = LoggerFactory.getLogger(QuartzJobServiceImpl.class);
  4. @Autowired
  5. private Scheduler scheduler;
  6. @Override
  7. public void addJob(String clazzName, String jobName, String groupName, String cronExp, Map<String, Object> param) {
  8. try {
  9. // 启动调度器,默认初始化的时候已经启动
  10. // scheduler.start();
  11. //构建job信息
  12. Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(clazzName);
  13. JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, groupName).build();
  14. //表达式调度构建器(即任务执行的时间)
  15. CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp);
  16. //按新的cronExpression表达式构建一个新的trigger
  17. CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, groupName).withSchedule(scheduleBuilder).build();
  18. //获得JobDataMap,写入数据
  19. if (param != null) {
  20. trigger.getJobDataMap().putAll(param);
  21. }
  22. scheduler.scheduleJob(jobDetail, trigger);
  23. } catch (Exception e) {
  24. log.error("创建任务失败", e);
  25. }
  26. }
  27. @Override
  28. public void pauseJob(String jobName, String groupName) {
  29. try {
  30. scheduler.pauseJob(JobKey.jobKey(jobName, groupName));
  31. } catch (SchedulerException e) {
  32. log.error("暂停任务失败", e);
  33. }
  34. }
  35. @Override
  36. public void resumeJob(String jobName, String groupName) {
  37. try {
  38. scheduler.resumeJob(JobKey.jobKey(jobName, groupName));
  39. } catch (SchedulerException e) {
  40. log.error("恢复任务失败", e);
  41. }
  42. }
  43. @Override
  44. public void runOnce(String jobName, String groupName) {
  45. try {
  46. scheduler.triggerJob(JobKey.jobKey(jobName, groupName));
  47. } catch (SchedulerException e) {
  48. log.error("立即运行一次定时任务失败", e);
  49. }
  50. }
  51. @Override
  52. public void updateJob(String jobName, String groupName, String cronExp, Map<String, Object> param) {
  53. try {
  54. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, groupName);
  55. CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
  56. if (cronExp != null) {
  57. // 表达式调度构建器
  58. CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp);
  59. // 按新的cronExpression表达式重新构建trigger
  60. trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
  61. }
  62. //修改map
  63. if (param != null) {
  64. trigger.getJobDataMap().putAll(param);
  65. }
  66. // 按新的trigger重新设置job执行
  67. scheduler.rescheduleJob(triggerKey, trigger);
  68. } catch (Exception e) {
  69. log.error("更新任务失败", e);
  70. }
  71. }
  72. @Override
  73. public void deleteJob(String jobName, String groupName) {
  74. try {
  75. //暂停、移除、删除
  76. scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, groupName));
  77. scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, groupName));
  78. scheduler.deleteJob(JobKey.jobKey(jobName, groupName));
  79. } catch (Exception e) {
  80. log.error("删除任务失败", e);
  81. }
  82. }
  83. @Override
  84. public void startAllJobs() {
  85. try {
  86. scheduler.start();
  87. } catch (Exception e) {
  88. log.error("开启所有的任务失败", e);
  89. }
  90. }
  91. @Override
  92. public void pauseAllJobs() {
  93. try {
  94. scheduler.pauseAll();
  95. } catch (Exception e) {
  96. log.error("暂停所有任务失败", e);
  97. }
  98. }
  99. @Override
  100. public void resumeAllJobs() {
  101. try {
  102. scheduler.resumeAll();
  103. } catch (Exception e) {
  104. log.error("恢复所有任务失败", e);
  105. }
  106. }
  107. @Override
  108. public void shutdownAllJobs() {
  109. try {
  110. if (!scheduler.isShutdown()) {
  111. // 需谨慎操作关闭scheduler容器
  112. // scheduler生命周期结束,无法再 start() 启动scheduler
  113. scheduler.shutdown(true);
  114. }
  115. } catch (Exception e) {
  116. log.error("关闭所有的任务失败", e);
  117. }
  118. }
  119. }

3.9、编写 contoller 服务

  • 先创建一个请求参数实体类
  1. public class QuartzConfigDTO implements Serializable {
  2. private static final long serialVersionUID = 1L;
  3. /**
  4. * 任务名称
  5. */
  6. private String jobName;
  7. /**
  8. * 任务所属组
  9. */
  10. private String groupName;
  11. /**
  12. * 任务执行类
  13. */
  14. private String jobClass;
  15. /**
  16. * 任务调度时间表达式
  17. */
  18. private String cronExpression;
  19. /**
  20. * 附加参数
  21. */
  22. private Map<String, Object> param;
  23. public String getJobName() {
  24. return jobName;
  25. }
  26. public QuartzConfigDTO setJobName(String jobName) {
  27. this.jobName = jobName;
  28. return this;
  29. }
  30. public String getGroupName() {
  31. return groupName;
  32. }
  33. public QuartzConfigDTO setGroupName(String groupName) {
  34. this.groupName = groupName;
  35. return this;
  36. }
  37. public String getJobClass() {
  38. return jobClass;
  39. }
  40. public QuartzConfigDTO setJobClass(String jobClass) {
  41. this.jobClass = jobClass;
  42. return this;
  43. }
  44. public String getCronExpression() {
  45. return cronExpression;
  46. }
  47. public QuartzConfigDTO setCronExpression(String cronExpression) {
  48. this.cronExpression = cronExpression;
  49. return this;
  50. }
  51. public Map<String, Object> getParam() {
  52. return param;
  53. }
  54. public QuartzConfigDTO setParam(Map<String, Object> param) {
  55. this.param = param;
  56. return this;
  57. }
  58. }
  • 编写 web 服务接口
  1. @RestController
  2. @RequestMapping("/test")
  3. public class TestController {
  4. private static final Logger log = LoggerFactory.getLogger(TestController.class);
  5. @Autowired
  6. private QuartzJobService quartzJobService;
  7. /**
  8. * 添加新任务
  9. * @param configDTO
  10. * @return
  11. */
  12. @RequestMapping("/addJob")
  13. public Object addJob(@RequestBody QuartzConfigDTO configDTO) {
  14. quartzJobService.addJob(configDTO.getJobClass(), configDTO.getJobName(), configDTO.getGroupName(), configDTO.getCronExpression(), configDTO.getParam());
  15. return HttpStatus.OK;
  16. }
  17. /**
  18. * 暂停任务
  19. * @param configDTO
  20. * @return
  21. */
  22. @RequestMapping("/pauseJob")
  23. public Object pauseJob(@RequestBody QuartzConfigDTO configDTO) {
  24. quartzJobService.pauseJob(configDTO.getJobName(), configDTO.getGroupName());
  25. return HttpStatus.OK;
  26. }
  27. /**
  28. * 恢复任务
  29. * @param configDTO
  30. * @return
  31. */
  32. @RequestMapping("/resumeJob")
  33. public Object resumeJob(@RequestBody QuartzConfigDTO configDTO) {
  34. quartzJobService.resumeJob(configDTO.getJobName(), configDTO.getGroupName());
  35. return HttpStatus.OK;
  36. }
  37. /**
  38. * 立即运行一次定时任务
  39. * @param configDTO
  40. * @return
  41. */
  42. @RequestMapping("/runOnce")
  43. public Object runOnce(@RequestBody QuartzConfigDTO configDTO) {
  44. quartzJobService.runOnce(configDTO.getJobName(), configDTO.getGroupName());
  45. return HttpStatus.OK;
  46. }
  47. /**
  48. * 更新任务
  49. * @param configDTO
  50. * @return
  51. */
  52. @RequestMapping("/updateJob")
  53. public Object updateJob(@RequestBody QuartzConfigDTO configDTO) {
  54. quartzJobService.updateJob(configDTO.getJobName(), configDTO.getGroupName(), configDTO.getCronExpression(), configDTO.getParam());
  55. return HttpStatus.OK;
  56. }
  57. /**
  58. * 删除任务
  59. * @param configDTO
  60. * @return
  61. */
  62. @RequestMapping("/deleteJob")
  63. public Object deleteJob(@RequestBody QuartzConfigDTO configDTO) {
  64. quartzJobService.deleteJob(configDTO.getJobName(), configDTO.getGroupName());
  65. return HttpStatus.OK;
  66. }
  67. /**
  68. * 启动所有任务
  69. * @return
  70. */
  71. @RequestMapping("/startAllJobs")
  72. public Object startAllJobs() {
  73. quartzJobService.startAllJobs();
  74. return HttpStatus.OK;
  75. }
  76. /**
  77. * 暂停所有任务
  78. * @return
  79. */
  80. @RequestMapping("/pauseAllJobs")
  81. public Object pauseAllJobs() {
  82. quartzJobService.pauseAllJobs();
  83. return HttpStatus.OK;
  84. }
  85. /**
  86. * 恢复所有任务
  87. * @return
  88. */
  89. @RequestMapping("/resumeAllJobs")
  90. public Object resumeAllJobs() {
  91. quartzJobService.resumeAllJobs();
  92. return HttpStatus.OK;
  93. }
  94. /**
  95. * 关闭所有任务
  96. * @return
  97. */
  98. @RequestMapping("/shutdownAllJobs")
  99. public Object shutdownAllJobs() {
  100. quartzJobService.shutdownAllJobs();
  101. return HttpStatus.OK;
  102. }
  103. }

3.10、服务接口测试

运行 SpringBoot 的Application类,启动服务!

 创建一个每5秒钟执行一次的定时任务

 

可以看到服务正常运行!

3.11、注册监听器(选用)

当然,如果你想在 SpringBoot 里面集成 Quartz 的监听器,操作也很简单!

  • 创建任务调度监听器
  1. @Component
  2. public class SimpleSchedulerListener extends SchedulerListenerSupport {
  3. @Override
  4. public void jobScheduled(Trigger trigger) {
  5. System.out.println("任务被部署时被执行");
  6. }
  7. @Override
  8. public void jobUnscheduled(TriggerKey triggerKey) {
  9. System.out.println("任务被卸载时被执行");
  10. }
  11. @Override
  12. public void triggerFinalized(Trigger trigger) {
  13. System.out.println("任务完成了它的使命,光荣退休时被执行");
  14. }
  15. @Override
  16. public void triggerPaused(TriggerKey triggerKey) {
  17. System.out.println(triggerKey + "(一个触发器)被暂停时被执行");
  18. }
  19. @Override
  20. public void triggersPaused(String triggerGroup) {
  21. System.out.println(triggerGroup + "所在组的全部触发器被停止时被执行");
  22. }
  23. @Override
  24. public void triggerResumed(TriggerKey triggerKey) {
  25. System.out.println(triggerKey + "(一个触发器)被恢复时被执行");
  26. }
  27. @Override
  28. public void triggersResumed(String triggerGroup) {
  29. System.out.println(triggerGroup + "所在组的全部触发器被回复时被执行");
  30. }
  31. @Override
  32. public void jobAdded(JobDetail jobDetail) {
  33. System.out.println("一个JobDetail被动态添加进来");
  34. }
  35. @Override
  36. public void jobDeleted(JobKey jobKey) {
  37. System.out.println(jobKey + "被删除时被执行");
  38. }
  39. @Override
  40. public void jobPaused(JobKey jobKey) {
  41. System.out.println(jobKey + "被暂停时被执行");
  42. }
  43. @Override
  44. public void jobsPaused(String jobGroup) {
  45. System.out.println(jobGroup + "(一组任务)被暂停时被执行");
  46. }
  47. @Override
  48. public void jobResumed(JobKey jobKey) {
  49. System.out.println(jobKey + "被恢复时被执行");
  50. }
  51. @Override
  52. public void jobsResumed(String jobGroup) {
  53. System.out.println(jobGroup + "(一组任务)被恢复时被执行");
  54. }
  55. @Override
  56. public void schedulerError(String msg, SchedulerException cause) {
  57. System.out.println("出现异常" + msg + "时被执行");
  58. cause.printStackTrace();
  59. }
  60. @Override
  61. public void schedulerInStandbyMode() {
  62. System.out.println("scheduler被设为standBy等候模式时被执行");
  63. }
  64. @Override
  65. public void schedulerStarted() {
  66. System.out.println("scheduler启动时被执行");
  67. }
  68. @Override
  69. public void schedulerStarting() {
  70. System.out.println("scheduler正在启动时被执行");
  71. }
  72. @Override
  73. public void schedulerShutdown() {
  74. System.out.println("scheduler关闭时被执行");
  75. }
  76. @Override
  77. public void schedulerShuttingdown() {
  78. System.out.println("scheduler正在关闭时被执行");
  79. }
  80. @Override
  81. public void schedulingDataCleared() {
  82. System.out.println("scheduler中所有数据包括jobs, triggers和calendars都被清空时被执行");
  83. }
  84. }
  • 创建任务触发监听器
  1. @Component
  2. public class SimpleTriggerListener extends TriggerListenerSupport {
  3. /**
  4. * Trigger监听器的名称
  5. * @return
  6. */
  7. @Override
  8. public String getName() {
  9. return "mySimpleTriggerListener";
  10. }
  11. /**
  12. * Trigger被激发 它关联的job即将被运行
  13. * @param trigger
  14. * @param context
  15. */
  16. @Override
  17. public void triggerFired(Trigger trigger, JobExecutionContext context) {
  18. System.out.println("myTriggerListener.triggerFired()");
  19. }
  20. /**
  21. * Trigger被激发 它关联的job即将被运行, TriggerListener 给了一个选择去否决 Job 的执行,如果返回TRUE 那么任务job会被终止
  22. * @param trigger
  23. * @param context
  24. * @return
  25. */
  26. @Override
  27. public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
  28. System.out.println("myTriggerListener.vetoJobExecution()");
  29. return false;
  30. }
  31. /**
  32. * 当Trigger错过被激发时执行,比如当前时间有很多触发器都需要执行,但是线程池中的有效线程都在工作,
  33. * 那么有的触发器就有可能超时,错过这一轮的触发。
  34. * @param trigger
  35. */
  36. @Override
  37. public void triggerMisfired(Trigger trigger) {
  38. System.out.println("myTriggerListener.triggerMisfired()");
  39. }
  40. /**
  41. * 任务完成时触发
  42. * @param trigger
  43. * @param context
  44. * @param triggerInstructionCode
  45. */
  46. @Override
  47. public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
  48. System.out.println("myTriggerListener.triggerComplete()");
  49. }
  50. }
  • 创建任务执行监听器
  1. @Component
  2. public class SimpleJobListener extends JobListenerSupport {
  3. /**
  4. * job监听器名称
  5. * @return
  6. */
  7. @Override
  8. public String getName() {
  9. return "mySimpleJobListener";
  10. }
  11. /**
  12. * 任务被调度前
  13. * @param context
  14. */
  15. @Override
  16. public void jobToBeExecuted(JobExecutionContext context) {
  17. System.out.println("simpleJobListener监听器,准备执行:"+context.getJobDetail().getKey());
  18. }
  19. /**
  20. * 任务调度被拒了
  21. * @param context
  22. */
  23. @Override
  24. public void jobExecutionVetoed(JobExecutionContext context) {
  25. System.out.println("simpleJobListener监听器,取消执行:"+context.getJobDetail().getKey());
  26. }
  27. /**
  28. * 任务被调度后
  29. * @param context
  30. * @param jobException
  31. */
  32. @Override
  33. public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
  34. System.out.println("simpleJobListener监听器,执行结束:"+context.getJobDetail().getKey());
  35. }
  36. }
  • 最后,将监听器注册到Scheduler
  1. @Autowired
  2. private SimpleSchedulerListener simpleSchedulerListener;
  3. @Autowired
  4. private SimpleJobListener simpleJobListener;
  5. @Autowired
  6. private SimpleTriggerListener simpleTriggerListener;
  7. @Bean(name = "scheduler")
  8. public Scheduler scheduler() throws IOException, SchedulerException {
  9. Scheduler scheduler = schedulerFactoryBean().getScheduler();
  10. //全局添加监听器
  11. //添加SchedulerListener监听器
  12. scheduler.getListenerManager().addSchedulerListener(simpleSchedulerListener);
  13. // 添加JobListener, 支持带条件匹配监听器
  14. scheduler.getListenerManager().addJobListener(simpleJobListener, KeyMatcher.keyEquals(JobKey.jobKey("myJob", "myGroup")));
  15. // 添加triggerListener,设置全局监听
  16. scheduler.getListenerManager().addTriggerListener(simpleTriggerListener, EverythingMatcher.allTriggers());
  17. return scheduler;
  18. }

3.12、采用项目数据源(选用)

在上面的 Quartz 数据源配置中,我们使用了自定义的数据源,目的是和项目中的数据源实现解耦,当然有的同学不想单独建库,想和项目中数据源保持一致,配置也很简单!

  • quartz.properties配置文件中,去掉org.quartz.jobStore.dataSource配置
  1. #注释掉quartz的数据源配置
  2. #org.quartz.jobStore.dataSource=qzDS
  • QuartzConfig配置类中加入dataSource数据源,并将其注入到quartz
  1. @Autowired
  2. private DataSource dataSource;
  3. @Bean
  4. public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
  5. //...
  6. SchedulerFactoryBean factory = new SchedulerFactoryBean();
  7. factory.setQuartzProperties(propertiesFactoryBean.getObject());
  8. //使用数据源,自定义数据源
  9. factory.setDataSource(dataSource);
  10. //...
  11. return factory;
  12. }

四、任务调度测试

在实际的部署中,项目都是集群进行部署,因此为了和正式环境一致,我们再新建两个相同的项目来测试一下在集群环境下 quartz 是否可以实现分布式调度,保证任何一个定时任务只有一台机器在运行

理论上,我们只需要将刚刚新建好的项目,重新复制一份,然后修改一下端口号就可以实现本地测试!

因为curd服务只需要一个,因此我们不需要再编写QuartzJobService等增、删、改服务,仅仅保持QuartzConfigDruidConnectionProviderQuartzJobFactoryTfCommandJobquartz.properties类和配置都是相同的就可以了!

  • 依次启动服务quartz-001quartz-002quartz-003,看看效果如何

第一个启动的服务quartz-001会优先加载数据库中已经配置好的定时任务,其他两个服务quartz-002quartz-003都没有主动调度服务

  • 当我们主动关闭quartz-001quartz-002服务主动接收任务调度 

  • 当我们主动关闭quartz-002,同样quartz-003服务主动接收任务调度

最终结果,和我们预期效果一致!

五、小结

本文主要围绕springboot + quartz + mysql实现持久化分布式调度进行介绍,所有的代码功能,都测试过。

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/981735
推荐阅读
相关标签
  

闽ICP备14008679号