<< 一个解析日期时间格式的Java类源代码 | 首页 | 我收藏的链接(27) >>

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

虽然在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>

标签 : , ,


Avatar: ellen

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

前辈,你这篇文章就是及时雨啊。我这几天正在搞这个jobstore。可是现在遇到个问题,放狗都搜不到什么东西。 008-10-08 15:48:09,806 org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:205) - Context initialization failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.scheduling.quartz.SchedulerFactoryBean' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is org.quartz.SchedulerException: Registration of jobs and triggers failed: closeResultSet Caused by: org.quartz.SchedulerException: Registration of jobs and triggers failed: closeResultSet at org.springframework.scheduling.quartz.SchedulerFactoryBean.registerJobsAndTriggers(SchedulerFactoryBean.java:798) 请前辈指点一二,叩谢
Avatar: ellen

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

啊,前辈,刚才那个问题莫名其妙就没了。现在是另外一个问题,我的xml定义的contrigger是这样的: 在applicationContext.xml里: <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="configLocation" value="/WEB-INF/quartz.properties" /> <property name="triggers"> <list> <ref bean="cronTertioRespParseTrigger" /> </list> </property> </bean> 然后在具体模块的xml里定义了: <bean id="cronTertioRespParseTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail"> <bean class="com.foss.main.customObject.MyMethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="terRspParseCronService" /> <property name="targetMethod" value="processFiles" /> <!-- <property name="concurrent"><value>false</value></property> --> </bean> </property> <property name="volatility"><value>true</value></property> <property name="cronExpression"> <value>0 0/5 * * * ?</value> </property> </bean> 现在的问题是,当weblogic server起来后,报: 2008-10-08 16:40:00,107 org.quartz.core.JobRunShell.run(JobRunShell.java:202) - Calling execute on job DEFAULT.com.foss.main.customObject.MyMethodInvokingJobD etailFactoryBean#308737 2008-10-08 16:40:00,117 org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean$MethodInvokingJob.executeInternal(MethodInvokingJobDetailFact oryBean.java:242) - Could not invoke method 'null' on target object [null] java.lang.IllegalStateException: prepare() must be called prior to invoke() on MethodInvoker at org.springframework.util.MethodInvoker.invoke(MethodInvoker.java:267) at org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean$MethodInvokingJob.executeInternal(MethodInvokingJobDetailFactoryBean.java: 224) at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:86) at org.quartz.core.JobRunShell.run(JobRunShell.java:203) at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:520) 2008-10-08 16:40:00,157 org.quartz.core.JobRunShell.run(JobRunShell.java:208) - Job DEFAULT.com.foss.main.customObject.MyMethodInvokingJobDetailFactoryBean#30 8737 threw a JobExecutionException: org.quartz.JobExecutionException: Could not invoke method 'null' on target object [null] [See nested exception: java.lang.IllegalStateException: prepare() mus t be called prior to invoke() on MethodInvoker] 其中,MyMethodInvokingJobDetailFactoryBean是我wripper的MethodInvokingJobDetailFactoryBean 是不是我xml里config的job不对呢?请前辈指教

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

你的错误报:Could not invoke method 'null' on target object [null] ; 说明的你的配置有问题。如果你是要集群环境跑,那就不能用MethodInvokingJobDetailFactoryBean,好像你的MyMethodInvokingJobDetailFactoryBean也是继承这个的。如果不是集群环境,那么用MethodInvokingJobDetailFactoryBean很方便。

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

如果不是集群环境参考:
<bean id="exampleJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="ecOwnUnstopWorksheetManager" />
<property name="targetMethod" value="execSyncPaymeny" />
<property name="concurrent" value="false" />
</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>
Avatar: ellen

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

大侠,我是要在集群环境跑。我的目的是要在cluster的环境下,同一时间只能有一个app server 跑scheduled 好的一个job, 我做的那个MyMethodInvokingJobDetailFactoryBean的确是继承的MethodInvokingJobDetailFactoryBean,因为MethodInvokingJobDetailFactoryBean不是序列化的。但是我在MyMethodInvokingJobDetailFactoryBean不知道该怎么重写execute()。。。。。。。 刚才又去试了你给的SysScheduleManagerImpl.java,可是在做ContextUtil的遇到路径问题。在我的applicationContext里,有定义quartz.property的路径: <property name="configLocation" value="WEB-INF/quartz.properties" /> 这个在weblogic parse xml file的时候是工作的。但是当用FileSystemXmlApplicationContext(xml file list)的时候,就找不到了。我要是改成FileSystemXmlApplicationContext能找到的,weblogic parse的时候就找不到了。晕啊

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

大侠,我终于搞出来了。没有用那个ContextUtil 而是在xml file里面加一句: <bean id="Scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="configLocation" value="WEB-INF/quartz.properties" /> <property name="applicationContextSchedulerContextKey"><value>applicationContext</value></property> <property name="triggers"> <list> 然后在SysScheduleManagerImpl.java里: ApplicationContext ctx = (ApplicationContext) context.getScheduler().getContext().get("applicationContext"); Object otargetObject=ctx.getBean(targetObject);

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

非常好

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

Thanks so much. Very helpful.

发表评论 发送引用通报