在Spring中使用Quartz任务调度支持集群

时间:2021-04-16 20:06:47


虽然在Quartz上有配置Quartz集群Clustering ,但是在Spring中使用Quartz任务调度并支持集群系统却有些问题,下面介绍解决办法:

环境:(环境非常重要,注意版本号)

Spring-1.2.7spring.jar-1.2.7.jar

Quartz-1.5.2quartz-1.5.2.jar,quartz-oracle-1.5.2.jar

Oracle10G:

org.springframework.scheduling.quartz.CronTriggerBean与Quartz版本依赖情况:
NOTE: This convenience subclass does not work with trigger persistence in Quartz 1.6, 
due to a change in Quartz's trigger handling. Use Quartz 1.5 if you rely on trigger 
persistence based on this class, or the standard Quartz CronTrigger class instead.
org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean在使用 
org.quartz.impl.jdbcjobstore.JobStoreTX
的支持情况:

Note:
JobDetails created via this FactoryBean are not serializable and thus not suitable

for persistent job stores. You need to implement your own Quartz Job as a thin wrapper for

each case where you want a persistent job to delegate to a specific service method.
所以,Quartz集群只支持JDBCJobStore存储方式,而MethodInvokingJobDetailFactoryBean不能序列化存储job数据到数据库,
所以需要手工编写任务调度类继承QuartzJobBean,否则报如下错误:
ERROR [org.springframework.web.context.ContextLoader] Context initialization failed
org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'schedulerFactoryBean' defined
in ServletContext resource [/WEB-INF/classes/tim-quartz.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.MethodInvoki ngJobDetailFactoryBean [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.MethodInvoki ngJobDetailFactoryBean]

类路径上的quartz.properties:

# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#

org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

org.quartz.jobStore.misfireThreshold = 60000

#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

 


#============================================================================
# Configure Main Scheduler Properties  
#============================================================================

org.quartz.scheduler.instanceId = AUTO


#============================================================================
# Configure JobStore  
#============================================================================

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = SYS_

org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000

#============================================================================
# Configure Datasources  
#============================================================================

#org.quartz.dataSource.myDS.jndiURL = java:comp/env/jdbc/psmis

org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@10.150.131.33:1521:psmis
org.quartz.dataSource.myDS.user = psmis
org.quartz.dataSource.myDS.password = psmis33
org.quartz.dataSource.myDS.maxConnections = 5
org.quartz.dataSource.myDS.validationQuery=select 0 from dual

 

创建数据库表结构:在下载的包quartz-1.5.2.zip\quartz-1.5.2\docs\dbTables\tables_oracle.sql

调度类,SysScheduleManagerImpl.java:

package com.sunrise.psmis.sysmanagement.service.impl;

import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;

import com.sunrise.psmis.webapp.util.ContextUtil;

public class SysScheduleManagerImpl extends QuartzJobBean{
 protected final Log logger = LogFactory.getLog(getClass());
 
 private String targetObject;
 private String targetMethod;
 
 protected void executeInternal(JobExecutionContext context) throws JobExecutionException{
 //  System.out.println(jobData.getData() + " 第一个已经被执行了!!");
  try
  {
         ApplicationContext ctx =ContextUtil.getContext();

         Object otargetObject=ctx.getBean(targetObject);
   
   Method m=null;
   try {
    m = otargetObject.getClass().getMethod(targetMethod, new Class[] {});
    
    m.invoke(otargetObject,  new Object[] {});
   } catch (SecurityException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (NoSuchMethodException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
         

  }
  catch(Exception e)
  {
   throw new JobExecutionException(e);
  }
  finally
  {
   logger.debug("end");
  }
  
 }

 public void setTargetObject(String targetObject) {
  this.targetObject = targetObject;
 }

 public void setTargetMethod(String targetMethod) {
  this.targetMethod = targetMethod;
 }

}

//ContextUtil类可以在应用启动的Listener里初始化Spring的ApplicationContext,将ApplicationContext保存在static变量里

applicationContext-service.xml:

  <bean id="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass">
     <value>com.sunrise.psmis.sysmanagement.service.impl.SysScheduleManagerImpl</value>
    </property>
  <property name="jobDataAsMap">
        <map>
            <entry key="targetObject" value="ecOwnUnstopWorksheetManager"/>
            <entry key="targetMethod" value="execSyncPaymeny"/>
        </map>
  </property>   
  </bean>
 <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
     <property name="jobDetail" ref="exampleJob" />
     <!-- run every morning at 6 AM -->
     <property name="cronExpression" value="0 2 * * * ?" />
 </bean>

  <bean id="seqJob" class="org.springframework.scheduling.quartz.JobDetailBean">
   <property name="jobClass">
    <value>com.sunrise.psmis.sysmanagement.service.impl.SysScheduleManagerImpl</value>
   </property>
  <property name="jobDataAsMap">
        <map>
            <entry key="targetObject" value="sysIdManager"/>
            <entry key="targetMethod" value="callProSeqEvalute"/>
        </map>
  </property>   
  </bean>
 
 <bean id="seqTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
     <property name="jobDetail" ref="seqJob" />
     <!-- run every morning at 6 AM -->
     <property name="cronExpression" value="0 0 0,1,2 * * ?" />
 </bean>

 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
     <property name="triggers">
         <list>
             <ref bean="cronTrigger" />   
             <ref bean="seqTrigger"/>
         </list>
     </property>
  <property name="schedulerContextAsMap">
   <map>
   <entry key="sysIdManager">
    <ref bean="sysIdManager" />
   </entry>
   <entry key="ecOwnUnstopWorksheetManager">
    <ref bean="ecOwnUnstopWorksheetManager" />
   </entry>
   </map>
  </property>
 </bean>