spring结合Quartz的集群功能实现

一:前沿

  在上一篇(http://www.cnblogs.com/wuhao1991/p/4331613.html)的博客中记载了定时的功能,但是集成是没有成功的,在这篇中,我在解释下这里的”集成的含义“,这里的集成是指:比如我有两个tomcat(tomcat1,tomcat2),然后现在我先启动tomcat1,在启动tomcat2,但是定时查询的功能在tomcat1上在执行,但是tomcat2上没有执行;此时我把tomcat1停止调,tomcat2又继续执行这个定时的功能;所以这里的集成说白了就是,定时的功能只能执行一个,这两个tomcat里卖弄是互斥的关系!

二:内容技术

  在上一篇中说过,我在配置集群的时候,老是出问题,那么现在我会把这个问题记载下来;下面进行说明吧!

application-quartz.xml配置如下

<?xml version="1.0" encoding="UTF-8" ?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xmlns:aop="http://www.springframework.org/schema/aop"  
       xmlns:tx="http://www.springframework.org/schema/tx"  
       xmlns:context="http://www.springframework.org/schema/context"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
    	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd  
    	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd  
    	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    	
    	<bean id="scheduTask" class="com.xpg.chargepile.quartz.ScheduTask"></bean>    
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    	<property name="triggers">
    		<list>
    			<bean class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    			 	<property name="cronExpression">
	          			<value>0/5 * * * * ?</value>
	        		</property>
	        		<property name="jobDetail">
	            		<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	            			<property name="targetObject" ref="scheduTask"></property>
			    			<property name="targetMethod" value="doTask"></property>
<!-- 			    			是否允许任务并发执行。当值为false时,表示必须等到前一个线程处理完毕后才再启一个新的线程 -->
							<property name="concurrent" value="false"/>
	            		</bean>
	       			 </property>
    			</bean>
    		</list>
    	</property>
    	 <property name="dataSource">
        	<ref bean="dataSource"/>
        </property>
        <property name="applicationContextSchedulerContextKey" value="applicationContextKey"></property>
        <property name="configLocation" value="classpath:quartz.properties"></property>
    	 <property name="autoStartup" value="true" />
    </bean>
    	
</beans>

 bug如下:

严重: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.scheduling.quartz.SchedulerFactoryBean#0' defined in file [D:\MyEclipse\wh3\.metadata\.me_tcat7\webapps\chargepile\WEB-INF\classes\applicationContext-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.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]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1568)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:540)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:706)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4791)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5285)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:618)
    at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1100)
    at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1618)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
Caused by: 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]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1115)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$2.executeVoid(JobStoreSupport.java:1062)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3703)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3701)
    at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:245)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJobAndTrigger(JobStoreSupport.java:1058)
    at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:886)
    at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:249)
    at org.springframework.scheduling.quartz.SchedulerAccessor.addTriggerToScheduler(SchedulerAccessor.java:308)
    at org.springframework.scheduling.quartz.SchedulerAccessor.registerJobsAndTriggers(SchedulerAccessor.java:235)
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:512)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1627)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1564)
    ... 25 more
Caused by: 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
    at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.serializeJobData(StdJDBCDelegate.java:3083)
    at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.insertJobDetail(StdJDBCDelegate.java:606)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1112)
    ... 37 more

使得我照这个解决方案找了好久好久啊!所以看看下面的截图(http://www.objectpartners.com/2013/07/09/configuring-quartz-2-with-spring-in-clustered-mode/)

 就是看完这句话,我才把集成给实验成功的,现在附上正确的代码<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:aop="http://www.springframework.org/schema/aop"  
       xmlns:tx="http://www.springframework.org/schema/tx"  
       xmlns:context="http://www.springframework.org/schema/context"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd  
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        
        
        <bean id="jobTask" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
                <property name="jobClass" value="com.gsh.gradyate.quartz.FirstJob"/>
                  <property name="durability" value="true"/>
        </bean>
        
       <bean id="myJobTrigger"  class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <property name="jobDetail">
                <ref bean="jobTask" />
            </property>
            <property name="cronExpression">
                <value>1/5000 * * * * ?</value>
            </property>
    </bean>
    
     <!--  总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序  -->
    <!-- 如果lazy-init='true',则需要实例化该bean才能执行调度程序 -->
    <bean id="startQuertz" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
        
         <property name="schedulerContextAsMap">      
            <map>      
                <!-- spring 管理的service需要放到这里,才能够注入成功 -->      
                <description>schedulerContextAsMap</description>      
                <entry key="userDao" value-ref="userDao"/>      
            </map>      
        </property>      
        
        <property name="applicationContextSchedulerContextKey" value="applicationContextKey"></property>
        <property name="configLocation" value="classpath:quartz.properties"></property>
         <property name="autoStartup" value="true" />
        <property name="triggers">
            <list>
                <ref bean="myJobTrigger" />
            </list>
        </property>
    </bean>
<?xml version="1.0" encoding="UTF-8" ?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xmlns:aop="http://www.springframework.org/schema/aop"  
       xmlns:tx="http://www.springframework.org/schema/tx"  
       xmlns:context="http://www.springframework.org/schema/context"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd  
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        
        
        <bean id="scheduTask" class="com.xpg.chargepile.quartz.ScheduTask"></bean>
        
        <bean id="jobTask" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
                <property name="jobClass" value="com.xpg.chargepile.quartz.FirstJob"/>
         
    <property name="jobDataAsMap">

                 <map>

                              <!-- 非spring管理的service放到这里,就可以注入进去 -->
                                <description>jobDataAsMap</description>
                             <!-- key 属性值,value 对应的bean -->
                        <entry key="uploader" value-ref="uploader" />
                      </map>
                   </property>

        <property name="durability" value="true"/>

        </bean>
        
       <bean id="myJobTrigger"  class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <property name="jobDetail">
                <ref bean="jobTask" />
            </property>
            <property name="cronExpression">
                <value>1/5000 * * * * ?</value>
            </property>
    </bean>
    
     <!--  总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序  -->
    <!-- 如果lazy-init='true',则需要实例化该bean才能执行调度程序 -->
    <bean id="startQuertz" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
        
         <property name="schedulerContextAsMap">      
            <map>      
                <!-- spring 管理的service需要放到这里,才能够注入成功 -->      
                <description>schedulerContextAsMap</description>      
                <entry key="userDao" value-ref="userDao"/>      
            </map>      
        </property>      
        
        <property name="applicationContextSchedulerContextKey" value="applicationContextKey"></property>
        <property name="configLocation" value="classpath:quartz.properties"></property>
         <property name="autoStartup" value="true" />
        <property name="triggers">
            <list>
                <ref bean="myJobTrigger" />
            </list>
        </property>
    </bean>

firstjob.java:

package com.gsh.graduate.quartz;

import java.util.Date;

import org.quartz.JobExecutionContext;
import org.quartz.SchedulerContext;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

import com.gsh.graduate.dao.UserDao;
import com.gsh.graduate.pojo.User;
@Component
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class FirstJob extends QuartzJobBean {

// @AutoWire
 // private UserDao userDao
private UserDao userDao;
public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }

        @Override
        protected void executeInternal(JobExecutionContext context) {                  
            //获取JobExecutionContext中的service对象    
            try {
                SchedulerContext skedCtx = context.getScheduler().getContext();
                userDao=(UserDao) skedCtx.get("userDao");
            } catch (SchedulerException e) {
                e.printStackTrace();
            }      
            User u=userDao.getUserByID("12345");
            System.out.println("---"+u);
            System.out.println("定时开始0,1,2,3");
            System.out.println(new Date());
            System.out.println("定时结束,game over");
        }
}

特别说明:这里面的对象必须在配置文件里面声明,在firstjob.java类中,如果你想通过这样像代码中注释的那样,自动注入会报一下错误;所以可以看下spring 通过配置向quartz 注入service(http://blog.csdn.net/whaosy/article/details/6298686)

2015-03-12 14:47:20 ERROR org.quartz.core.JobRunShell.run(211) | Job DEFAULT.jobTask threw an unhandled Exception: 
java.lang.NullPointerException
    at com.gsh.graduate.quartz.FirstJob.executeInternal(FirstJob.java:53)
    at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
2015-03-12 14:47:20 ERROR org.quartz.core.ErrorLogger.schedulerError(2425) | Job (DEFAULT.jobTask threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: java.lang.NullPointerException]
    at org.quartz.core.JobRunShell.run(JobRunShell.java:213)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: java.lang.NullPointerException
    at com.gsh.graduate.quartz.FirstJob.executeInternal(FirstJob.java:53)
    at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
    ... 1 more

上面第一次实验的那种bug本来有两种配置方式的,但是我在配置好了之后,实验以前的没有报bug,我都郁闷了,附加代码看看

<?xml version="1.0" encoding="UTF-8" ?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xmlns:aop="http://www.springframework.org/schema/aop"  
       xmlns:tx="http://www.springframework.org/schema/tx"  
       xmlns:context="http://www.springframework.org/schema/context"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd  
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        
        <!---方案一->
        <bean id="scheduTask" class="com.gsh.graduate.quartz.ScheduTask"></bean>
        
<!--         调用的对象和方法的指定 -->
        <bean id="jobTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!--         <bean id="jobTask" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> -->
<!--                 <property name="jobClass" value="com.gsh.graduate.quartz.FirstJob"/> -->
<!--                   <property name="durability" value="true"/> -->
<!--                 是否允许任务并发执行。当值为false时,表示必须等到前一个线程处理完毕后才再启一个新的线程 -->
                <property name="concurrent" value="false"/>
                <property name="targetObject" ref="scheduTask"></property>
                <property name="targetMethod" value="doTask"></property>
        </bean>
        
       <bean id="myJobTrigger"  class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <property name="jobDetail">
                <ref bean="jobTask" />
            </property>
            <property name="cronExpression">
                <value>1/5000 * * * * ?</value>
            </property>
    </bean>
    
     <!--  总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序  -->
    <!-- 如果lazy-init='true',则需要实例化该bean才能执行调度程序 -->
<!--     <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> -->
    <bean id="startQuertz" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
        
         <property name="schedulerContextAsMap">      
            <map>      
                <!-- spring 管理的service需要放到这里,才能够注入成功 -->      
                <description>schedulerContextAsMap</description>      
                <entry key="userDao" value-ref="userDao"/>      
            </map>      
        </property>      
        <property name="applicationContextSchedulerContextKey" value="applicationContextKey"></property>
        <property name="configLocation" value="classpath:quartz.properties"></property>
         <property name="autoStartup" value="true" />
        <property name="triggers">
            <list>
                <ref bean="myJobTrigger" />
            </list>
        </property>
    </bean>
        
<!--方案2-->
   <!--  <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <bean class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
                     <property name="cronExpression">
                          <value>0/5 * * * * ?</value>
                    </property>
                    <property name="jobDetail">
                        <bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
                            <property name="targetObject" ref="scheduTask"></property>
                            <property name="targetMethod" value="doTask"></property>
                            是否允许任务并发执行。当值为false时,表示必须等到前一个线程处理完毕后才再启一个新的线程
                            <property name="concurrent" value="false"/>
                        </bean>
                        </property>
                </bean>
            </list>
        </property>
         <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
        <property name="applicationContextSchedulerContextKey" value="applicationContextKey"></property>
        <property name="configLocation" value="classpath:quartz.properties"></property>
         <property name="autoStartup" value="true" />
    </bean> -->
        
</beans>
这是所有的代码,两种方案

三:总结,折腾了这么久终于把集成给折腾成功了啊,这是接触新鲜的东西,就是不一样啊,解决问题的能力还是有涨进的啊!继续Go!Boy,今天一下在把有关Quartz的都写了!不枉我研究了两天啊!

posted @ 2015-03-12 14:59  郁闷的耗子  阅读(9638)  评论(1编辑  收藏  举报