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的都写了!不枉我研究了两天啊!