Spring+Quartz集群环境下定时调度的解决方案

Posted 心若不动,风又奈何;你若不伤,岁月无恙。

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring+Quartz集群环境下定时调度的解决方案相关的知识,希望对你有一定的参考价值。

 集群环境可能出现的问题

在上一篇博客我们介绍了如何在自己的项目中从无到有的添加了Quartz定时调度引擎,其实就是一个Quartz 和Spring的整合过程,很容易实现,但是我们现在企业中项目通常都是部署在集群环境中的,这样我们之前的定时调度就会出现问题了,因为我们的定时任务都加载在内存中的,每个集群节点中的调度器都会去执行,这就会存在重复执行和资源竞争的问题,那么如何来解决这样的问题呢,往下面看吧...

解决方案

在一般的企业中解决类似的问题一般都是在一个note上部署Quartz其他note不部署(或者是在其他几个机器加IP地址过滤),但是这样集群对于定时任务来说就没有什么意义了,而且存在着单点故障的隐患,也就是这台部署着Quartz的机器一旦挂了,我们的定时任务就停止服务了,这绝对不是我们想要的。

Quartz本身是支持集群的,我们通过Quartz的集群方式来解决这样的问题。

Quartz集群

 

 

虽然单个 Quartz 实例能给予你很好的 Job调度能力,但它不能令典型的企业需求,如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job,Quartz 集群势必成为你方言的一部分了,并且即使是其中一台机器在最糟的时间崩溃了也能确保所有的 Job 得到执行。 QuartzJob Scheduling Framework

 了解了Quartz集群的好处,接下来就对我们之前的工程进行改造,增加Quartz集群特性。

Quartz集群中节点依赖于数据库来传播 Scheduler 实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群

所以我们集群的第一步就是建立Quartz所需要的12张表:

1、建表

在quartz核心包里面通过quartz提供的建表语句建立相关表结构

 

生成的表结构如下

 

这几张表是用于存储任务信息,触发器,调度器,集群节点等信息

详细解释: 
QRTZ_CRON_TRIGGERS 存储Cron Trigger,包括Cron 表达式和时区信息 
QRTZ_PAUSED_TRIGGER_GRPS 存储已暂停的Trigger 组的信息 
QRTZ_LOCKS 存储程序的非观锁的信息(假如使用了悲观锁) 
QRTZ_JOB_LISTENERS 存储有关已配置的JobListener 的信息 
QRTZ_BLOG_TRIGGERS Trigger 作为Blob 类型存储(用于Quartz 用户用JDBC 创建他们自己定制的Trigger 类型,JobStore并不知道如何存储实例的时候) 
QRTZ_TRIGGERS 存储已配置的Trigger 的信息 
所有的表默认以前缀QRTZ_开始。可以通过在quartz.properties配置修改(org.quartz.jobStore.tablePrefix= QRTZ_)。 

共12张表的SQL脚本

  1 # # Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar # # In your Quartz properties file, you\'ll need to set 
  2 # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  3 #
  4 
  5 DROP TABLE IF EXISTS QRTZ_JOB_LISTENERS; DROP TABLE IF EXISTS QRTZ_TRIGGER_LISTENERS; DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS
  6   (
  7     JOB_NAME  VARCHAR(200) NOT NULL,
  8     JOB_GROUP VARCHAR(200) NOT NULL,
  9     DESCRIPTION VARCHAR(250) NULL,
 10     JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
 11     IS_DURABLE VARCHAR(1) NOT NULL,
 12     IS_VOLATILE VARCHAR(1) NOT NULL,
 13     IS_STATEFUL VARCHAR(1) NOT NULL,
 14     REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
 15     JOB_DATA BLOB NULL,
 16     PRIMARY KEY (JOB_NAME,JOB_GROUP)
 17 ); CREATE TABLE QRTZ_JOB_LISTENERS
 18   (
 19     JOB_NAME  VARCHAR(200) NOT NULL,
 20     JOB_GROUP VARCHAR(200) NOT NULL,
 21     JOB_LISTENER VARCHAR(200) NOT NULL,
 22     PRIMARY KEY (JOB_NAME,JOB_GROUP,JOB_LISTENER),
 23     FOREIGN KEY (JOB_NAME,JOB_GROUP)
 24         REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)
 25 ); CREATE TABLE QRTZ_TRIGGERS
 26   (
 27     TRIGGER_NAME VARCHAR(200) NOT NULL,
 28     TRIGGER_GROUP VARCHAR(200) NOT NULL,
 29     JOB_NAME  VARCHAR(200) NOT NULL,
 30     JOB_GROUP VARCHAR(200) NOT NULL,
 31     IS_VOLATILE VARCHAR(1) 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 (TRIGGER_NAME,TRIGGER_GROUP),
 44     FOREIGN KEY (JOB_NAME,JOB_GROUP)
 45         REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)
 46 ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS
 47   (
 48     TRIGGER_NAME VARCHAR(200) NOT NULL,
 49     TRIGGER_GROUP VARCHAR(200) 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 (TRIGGER_NAME,TRIGGER_GROUP),
 54     FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
 55         REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
 56 ); CREATE TABLE QRTZ_CRON_TRIGGERS
 57   (
 58     TRIGGER_NAME VARCHAR(200) NOT NULL,
 59     TRIGGER_GROUP VARCHAR(200) NOT NULL,
 60     CRON_EXPRESSION VARCHAR(200) NOT NULL,
 61     TIME_ZONE_ID VARCHAR(80),
 62     PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
 63     FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
 64         REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
 65 ); CREATE TABLE QRTZ_BLOB_TRIGGERS
 66   (
 67     TRIGGER_NAME VARCHAR(200) NOT NULL,
 68     TRIGGER_GROUP VARCHAR(200) NOT NULL,
 69     BLOB_DATA BLOB NULL,
 70     PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
 71     FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
 72         REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
 73 ); CREATE TABLE QRTZ_TRIGGER_LISTENERS
 74   (
 75     TRIGGER_NAME  VARCHAR(200) NOT NULL,
 76     TRIGGER_GROUP VARCHAR(200) NOT NULL,
 77     TRIGGER_LISTENER VARCHAR(200) NOT NULL,
 78     PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_LISTENER),
 79     FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
 80         REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
 81 ); CREATE TABLE QRTZ_CALENDARS
 82   (
 83     CALENDAR_NAME  VARCHAR(200) NOT NULL,
 84     CALENDAR BLOB NOT NULL,
 85     PRIMARY KEY (CALENDAR_NAME)
 86 ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
 87   (
 88     TRIGGER_GROUP  VARCHAR(200) NOT NULL, 
 89     PRIMARY KEY (TRIGGER_GROUP)
 90 ); CREATE TABLE QRTZ_FIRED_TRIGGERS
 91   (
 92     ENTRY_ID VARCHAR(95) NOT NULL,
 93     TRIGGER_NAME VARCHAR(200) NOT NULL,
 94     TRIGGER_GROUP VARCHAR(200) NOT NULL,
 95     IS_VOLATILE VARCHAR(1) NOT NULL,
 96     INSTANCE_NAME VARCHAR(200) NOT NULL,
 97     FIRED_TIME BIGINT(13) NOT NULL,
 98     PRIORITY INTEGER NOT NULL,
 99     STATE VARCHAR(16) NOT NULL,
100     JOB_NAME VARCHAR(200) NULL,
101     JOB_GROUP VARCHAR(200) NULL,
102     IS_STATEFUL VARCHAR(1) NULL,
103     REQUESTS_RECOVERY VARCHAR(1) NULL,
104     PRIMARY KEY (ENTRY_ID)
105 ); CREATE TABLE QRTZ_SCHEDULER_STATE
106   (
107     INSTANCE_NAME VARCHAR(200) NOT NULL,
108     LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
109     CHECKIN_INTERVAL BIGINT(13) NOT NULL,
110     PRIMARY KEY (INSTANCE_NAME)
111 ); CREATE TABLE QRTZ_LOCKS
112   (
113     LOCK_NAME  VARCHAR(40) NOT NULL, 
114     PRIMARY KEY (LOCK_NAME)
115 ); INSERT INTO QRTZ_LOCKS values(\'TRIGGER_ACCESS\'); INSERT INTO QRTZ_LOCKS values(\'JOB_ACCESS\'); INSERT INTO QRTZ_LOCKS values(\'CALENDAR_ACCESS\'); INSERT INTO QRTZ_LOCKS values(\'STATE_ACCESS\'); INSERT INTO QRTZ_LOCKS values(\'MISFIRE_ACCESS\'); commit;
116 --------------------- 
117 作者:默默同学 
118 来源:CSDN 
119 原文:https://blog.csdn.net/u012909738/article/details/74454810 

 

2、编写quartz.properties文件

建立 quartz.properties文件把它放在工程的 src 目录下,内容如下:

复制代码
  1 #============================================================================
  2 
  3 # Configure Main Scheduler Properties 
  4 
  5 #============================================================================
  6 
  7  
  8 
  9 org.quartz.scheduler.instanceName = Mscheduler
 10 
 11 org.quartz.scheduler.instanceId = AUTO
 12 
 13 org.quartz.jobStore.clusterCheckinInterval=20000
 14 
 15  
 16 
 17 #============================================================================
 18 
 19 # Configure ThreadPool 
 20 
 21 #============================================================================
 22 
 23  
 24 
 25 org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
 26 
 27 org.quartz.threadPool.threadCount = 3
 28 
 29 org.quartz.threadPool.threadPriority = 5
 30 
 31  
 32 
 33 #============================================================================
 34 
 35 # Configure JobStore 
 36 
 37 #============================================================================
 38 
 39  
 40 
 41 #org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
 42 
 43  
 44 
 45 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
 46 
 47 #org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 48 
 49 org.quartz.jobStore.useProperties = true
 50 
 51 #org.quartz.jobStore.dataSource = myDS
 52 
 53 org.quartz.jobStore.tablePrefix = QRTZ_
 54 
 55 org.quartz.jobStore.isClustered = true
 56 
 57 org.quartz.jobStore.maxMisfiresToHandleAtATime=1
 58 
 59 #============================================================================
 60 
 61 # Configure Datasources 
 62 
 63 #============================================================================
 64 
 65  
 66 
 67 #mysql
 68 
 69 #org.quartz.dataSource.myDS.driver = com.ibm.db2.jcc.DB2Driver
 70 
 71 #org.quartz.dataSource.myDS.URL = jdbc:db2://localhost:50000/db
 72 
 73 #org.quartz.dataSource.myDS.user = db2
 74 
 75 #org.quartz.dataSource.myDS.password = db2
 76 
 77 #org.quartz.dataSource.myDS.maxConnections = 5
 78 
 79  
 80 
 81 #oracle
 82 
 83 #org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver
 84 
 85 #org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@localhost:1521:orcl
 86 
 87 #org.quartz.dataSource.myDS.user = scott
 88 
 89 #org.quartz.dataSource.myDS.password = shao
 90 
 91 #org.quartz.dataSource.myDS.maxConnections = 5
 92 
 93  
 94 
 95 #For Tomcat
 96 
 97 org.quartz.jobStore.driverDelegateClass =org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
 98 
 99 #For Weblogic & Websphere
100 
101 #org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.WebLogicDelegate
102 
103 org.quartz.jobStore.useProperties = false
104 
105 org.quartz.jobStore.dataSource = myDS
106 
107  
108 
109  
110 
111 #JNDI MODE
112 
113 #For Tomcat
114 
115 org.quartz.dataSource.myDS.jndiURL=java:comp/env/jdbc/oracle
116 
117 #For Weblogic & Websphere
118 
119 #org.quartz.dataSource.myDS.jndiURL=jdbc/oracle
120 
121  
122 
123  
124 
125 #============================================================================
126 
127 # Configure Plugins
128 
129 #============================================================================
130 
131  
132 
133 #org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin
134 
135  
136 
137 #org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin
138 
139 #org.quartz.plugin.jobInitializer.fileNames = jobs.xml
140 
141 #org.quartz.plugin.jobInitializer.overWriteExistingJobs = true
142 
143 #org.quartz.plugin.jobInitializer.failOnFileNotFound = true
144 
145 #org.quartz.plugin.jobInitializer.scanInterval = 10
146 
147 #org.quartz.plugin.jobInitializer.wrapInUserTransaction = false 
复制代码

红色加粗部分是集群需要的配置

核心配置解释如下:

org.quartz.jobStore.class 属性为JobStoreTX, 
将任务持久化到数据中。因为集群中节点依赖于数据库来传播Scheduler实例的状态,你只能在使用JDBC JobStore 时应用Quartz 集群。 

org.quartz.jobStore.isClustered 属性为true,通知Scheduler实例要它参与到一个集群当中。 

org.quartz.jobStore.clusterCheckinInterval 

属性定义了Scheduler实例检入到数据库中的频率(单位:毫秒)。 
Scheduler 检查是否其他的实例到了它们应当检入的时候未检入; 
这能指出一个失败的Scheduler 实例,且当前Scheduler 会以此来接管任何执行失败并可恢复的Job。 
通过检入操作,Scheduler也会更新自身的状态记录。clusterChedkinInterval越小,Scheduler节点检查失败的Scheduler 实例就越频繁。默认值是 20000 (即20 秒) 

3、修改spring-time.xml文件

复制代码
 1 <?xmlversion="1.0"encoding="UTF-8"?>
 2 <!DOCTYPEbeansPUBLIC"-//SPRING//DTD BEAN//EN"
 3         "http://www.springframework.org/dtd/spring-beans.dtd">
 4 <beans>
 5  
 6      <!-- 调度器lazy-init=\'false\'那么容器启动就会执行调度程序 -->
 7      <beanid="startQuertz"lazy-init="false"autowire="no"class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
 8         <property name="dataSource" ref="dataSource"/>
 9         <property name="configLocation" value="classpath:quartz.properties" />
10          <propertyname="triggers">
11               <list>
12                    <refbean="doTime"/>
13               </list>
14          </property>
15          <!-- 允许在Quartz上下文中使用Spring实例工厂 -->
16          <propertyname="applicationContextSchedulerContextKey"value="applicationContext"/>
17      </bean>
18     
19      <!-- 触发器 -->
20      <beanid="doTime"class="org.springframework.scheduling.quartz.CronTriggerBean">
21          <propertyname="jobDetail"ref="jobtask"></property>
22          <!-- cron表达式 -->
23          <propertyname="cronExpression"value="10,15,20,25,30,35,40,45,50,55 * * * * ?"></property>
24      </bean>
25     
26      <!-- 任务 -->
27      <beanid="jobtask"class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
28          <propertyname="targetObject"ref="synUsersJob"></property>
29          <propertyname="targetMethod"value="execute"></property>
30      </bean>
31     
32      <!-- 要调用的工作类 -->
33      <beanid="synUsersJob"class="org.leopard.core.quartz.job.SynUsersJob"></bean>
34       
35 </beans>
复制代码

增加红色加粗部分代码,注入数据源和加载quartz.properties文件

OK Quartz集群的配置只有这几步,我们来启动项目。。。

我们启着启着….报错了!

17:00:59,718 ERROR ContextLoader:215 - Context initialization failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name \'startQuertz\' defined in class path resource [config/spring/spring-time.xml]: Invocation of init method failed; nested exception is org.quartz.JobPersistenceException:Couldn\'t store job: Unable to serialize JobDataMap for insertion into database because the value of property \'methodInvoker\' is not serializable: org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean [See nested exception: java.io.NotSerializableException: Unable to serialize JobDataMap for insertion into database because the value of property \'methodInvoker\' is not serializable: org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean]

 

我们主要来看红色部分,主要原因就是这个MethodInvokingJobDetailFactoryBean 类中的 methodInvoking 方法,是不支持序列化的,因此在把 quartz 的 task 序列化进入数据库时就会抛这个serializable的错误

4、解决serializable错误解决方案

网上查了一下,解决这个问题,目前主要有两种方案:

4.1.修改Spring的源码

博客地址:http://jira.springframework.org/browse/SPR-3797

作者重写了MethodInvokingJobDetailFactoryBean

 

4.2.通过AOP反射对Spring源码进行切面重构

博客地址:http://blog.csdn.net/lifetragedy/article/details/6212831

根据 QuartzJobBean 来重写一个自己的类,然后使用 SPRING 把这个重写的类(我们就名命它为: MyDetailQuartzJobBean )注入 appContext 中后,再使用 AOP 技术反射出原有的 quartzJobx( 就是开发人员原来已经做好的用于执行 QUARTZ 的 JOB 的执行类 ) 。

 

两种方式我都进行了测试,都可以解决问题,我们这里先通过第二种方式解决这个bug,没有修改任何Spring的源码

4.2.1、增加MyDetailQuartzJobBean.Java

复制代码
 1 package org.leopard.core.quartz;
 2  
 3 import java.lang.reflect.Method;
 4  
 5 import org.apache.commons.logging.Log;
 6 import org.apache.commons.logging.LogFactory;
 7 import org.quartz.JobExecutionContext;
 8 import org.quartz.JobExecutionException;
 9 import org.springframework.context.ApplicationContext;
10 import org.springframework.scheduling.quartz.QuartzJobBean;
11  
12 /**
13  * 解决Spring和Quartz整合bug
14  *
15  */
16 public class MyDetailQuartzJobBean extends QuartzJobBean {
17          protected final Log logger = LogFactory.getLog(getClass());
18  
19          private String targetObject;
20          private String targetMethod;
21          private ApplicationContext ctx;
22  
23          protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
24                    try {
25  
26                             logger.info("execute [" + targetObject + "] at once>>>>>>");
27                             Object otargetObject = ctx.getBean(targetObject);
28                             Method m = null;
29                             try {
30                                      m = otargetObject.getClass().getMethod(targetMethod, new Class[] {});
31  
32                                      m.invoke(otargetObject, new Object[] {});
33                             } catch (SecurityException e) {
34                                      logger.error(e);
35                             } catch (NoSuchMethodException e) {
36                                      logger.error(e);
37                             }
38  
39                    } catch (Exception e) {
40                             throw new JobExecutionException(e);
41                    }
42  
43          }
44  
45          public void setApplicationContext(ApplicationContext applicationContext) {
46                    this.ctx = applicationContext;
47          }
48  
49          public void setTargetObject(String targetObject) {
50                    this.targetObject = targetObject;
51          }
52  
53          public void setTargetMethod(String targetMethod) {
54                    this.targetMethod = targetMethod;
55          }
56  
57 }
复制代码

5、再次修改spring-time.xml文件解决serializable问题

修改后的spring-time.xml文件内容如下:

复制代码
 1 <?xmlversion="1.0"encoding="UTF-8"?>
 2 <!DOCTYPEbeansPUBLIC"-//SPRING//DTD BEAN//EN"
 3         "http://www.springframework.org/dtd/spring-beans.dtd">
 4 <beans>
 5  
 6     <!-- 调度器lazy-init=\'false\'那么容器启动就会执行调度程序 -->
 7     <beanid="startQuertz"lazy-init="false"autowire="no"class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
 8         <propertyname="dataSource"ref="dataSource"/>
 9         <propertyname="configLocation"value="classpath:quartz.properties"/>
10         <propertyname="triggers">
11             <list>
12                 <refbean="doTime"/>
13             </list>
14         </property>
15         <!--这个是必须的,QuartzScheduler延时启动,应用启动完后 QuartzScheduler再启动-->
16         <propertyname="startupDelay"value="30"/>     
17         <!--这个是可选,QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了-->
18         <propertyname="overwriteExistingJobs"value="true"/>
19         <!-- 允许在Quartz上下文中使用Spring实例工厂 -->
20         <propertyname="applicationContextSchedulerContextKey"value="applicationContext"/>
21     </bean>
22    
23     <!-- 触发器 -->
24     <beanid="doTime"class="org.springframework.scheduling.quartz.CronTriggerBean">
25         <propertyname="jobDetail"ref="jobtask"></property>
26         <!-- cron表达式 -->
27         <propertyname="cronExpression"value="10,15,20,25,30,35,40,45,50,55 * * * * ?"></property>
28     </bean>
29    
30     <!-- 任务 -->
31     <beanid="jobtask"class="org.springframework.scheduling.quartz.JobDetailBean">
32         <propertyname="jobClass">
33              <value>org.leopard.core.quartz.MyDetailQuartzJobBean</value>
34         </property>
35         <propertyname="jobDataAsMap">
36             <map>
37                   <entrykey="targetObject"value="synUsersJob"/>
38                   <entrykey="targetMethod"value="execute"/>
39             </map>
40         </property>
41     </bean>
42    
43     <!-- 要调用的工作类 -->
44     <beanid="synUsersJob"class="org.leopard.core.quartz.job.SynUsersJob"></bean>
45      
46 </beans> 
复制代码

主要看红色加粗部分...

测试

Ok  配置完成,我们把oa_ssh部署到两台tomcat上面,分别启动。

 

 

 

可以看到我们先启动的tomcat控制台打印出日志

 

 

另外一台没有打印日志

 

 

 

这时我们把执行定时任务的那台tomcat停止,可以看到等了一会之后,我们的另外一台tomcat会把之前tomcat执行的定时任务接管过来继续执行,我们的集群是成功的。

参考网址:

https://www.cnblogs.com/yinfengjiujian/p/8670026.html

https://blog.csdn.net/u012909738/article/details/74454810

以上是关于Spring+Quartz集群环境下定时调度的解决方案的主要内容,如果未能解决你的问题,请参考以下文章

Java技术分享:集群环境下的定时任务

集群环境下定时任务调度问题与方案探讨

quartz集群分布式(并发)部署解决方案-Spring

JavaSpringboot-Quartz-分布式任务调度

Spring整合Quartz定时任务 在集群分布式系统中的应用(Mysql数据库环境)

集群式Quartz定时任务框架实践