Spring+Quartz的集群配置

来源:互联网 发布:网络用语然是什么意思 编辑:程序博客网 时间:2024/05/21 15:01

应用场景: 集群环境. 在多台服务器布置了相同的程序,程序中都包含有相同的定时器任务.如果不做处理,则会出现定时任务重复执行的问题,一般的业务场景这个是必须要避免的.

解决办法: 一. 只在一台服务器发布带有定时器的程序,其他的全都去除定时任务再发布. (如果这台服务器宕掉了,那就悲剧了) ; 二. 采用Quratz开源定时任务框架,并配置集群.(好处在于spring集成了对Quratz的支持,如果你也用了spring+Quratz,往下看).

本用例采用框架版本: Spring 3.0 + Quratz 1.8.5 参考:http://soulshard.iteye.com/blog/337886

起初采用spring对Quratz的原生支持,但是报错. 异常信息:

1
java.io.NotSerializableException: Unable to serialize JobDataMapfor insertion into database because the value of property'methodInvoker' is not serializable: org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean

据说是spring的bug,按照以上地址里给的解决办法,重写了MethodInvokingJobDetailFactoryBean. 但诡异的是,仍然报出 java.io.NotSerializableException,由于我的定时器里引用了Service层,这里抛出的是dataSource is not serializable.本以为是因为序列化的时候把Dao属性也获取并序列化了,所以就把getDao()都去掉了,但是还是会报错,看来不是按照这个逻辑执行程序的,按照上面地址里说的方案之一,说可以把service和Dao层都继承Serializable,但是我放弃了,因为我没办法把dataSource也继承 Serializable. 之后看了下面的留言,有一位仁兄写好了demo,并分享了出来.地址:http://download.csdn.net/source/2777549 OK,下面说详细代码.因为我下了那个demo之后还弄了好一会儿,有必要记录一下.

解决思路: 编写一个带有String类型属性的,实现Serializable的伪任务类,执行任务调度时,调用此类的实例,并注入真正要调用的类的Bean ID.也就是说,初始化任务的时候,注入的是这个类,而类中调用真正要执行的任务.从而避免了序列化问题.

伪任务类PseudoJob.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
</pre>
public class BootstrapJob implementsSerializable{
  
private static final long serialVersionUID = 4195235887559214105L;
private String targetJob ;
  
public void execute(ApplicationContext cxt) {
Job job = (Job)cxt.getBean(this.targetJob);
job.execute() ;
}
  
public String getTargetJob() {
return targetJob;
}
  
public void setTargetJob(String targetJob) {
this.targetJob = targetJob;
}
}

Job接口类:定时任务类必须继承Job接口

1
2
3
4
5
6
public interface Job extendsSerializable{
  
void execute();
  
}

MethodInvokingJobDetailFactoryBean

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.StatefulJob;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.util.MethodInvoker;
public class MethodInvokingJobDetailFactoryBean implementsFactoryBean, BeanNameAware, InitializingBean
{
privateLog logger = LogFactory.getLog(getClass());
privateString group = Scheduler.DEFAULT_GROUP;
privateboolean concurrent = true;
privateboolean durable = false;
privateboolean volatility = false;
privateboolean shouldRecover =false;
privateString[] jobListenerNames;
privateString beanName;
privateJobDetail jobDetail;
privateString targetClass;
privateObject targetObject;
privateString targetMethod;
privateString staticMethod;
privateObject[] arguments;
public String getTargetClass()
{
return targetClass;
}
public void setTargetClass(String targetClass)
{
this.targetClass = targetClass;
}
public String getTargetMethod()
{
return targetMethod;
}
public void setTargetMethod(String targetMethod)
{
this.targetMethod = targetMethod;
}
public Object getObject() throws Exception
{
return jobDetail;
}
public Class getObjectType()
{
return JobDetail.class;
}
public boolean isSingleton()
{
return true;
}
public void setBeanName(String beanName)
{
this.beanName = beanName;
}
public void afterPropertiesSet() throwsException
{
try
{
logger.debug("start");
  
logger.debug("Creating JobDetail "+beanName);
jobDetail = new JobDetail();
jobDetail.setName(beanName);
jobDetail.setGroup(group);
jobDetail.setJobClass(concurrent ? MethodInvokingJob.class: StatefulMethodInvokingJob.class);
jobDetail.setDurability(durable);
jobDetail.setVolatility(volatility);
jobDetail.setRequestsRecovery(shouldRecover);
if(targetClass!=null)
jobDetail.getJobDataMap().put("targetClass", targetClass);
if(targetObject!=null)
jobDetail.getJobDataMap().put("targetObject", targetObject);
if(targetMethod!=null)
jobDetail.getJobDataMap().put("targetMethod", targetMethod);
if(staticMethod!=null)
jobDetail.getJobDataMap().put("staticMethod", staticMethod);
if(arguments!=null)
jobDetail.getJobDataMap().put("arguments", arguments);
  
logger.debug("Registering JobListener names with JobDetail object "+beanName);
if (this.jobListenerNames !=null) {
for (int i = 0; i < this.jobListenerNames.length; i++) {
this.jobDetail.addJobListener(this.jobListenerNames[i]);
}
}
logger.info("Created JobDetail: "+jobDetail+"; targetClass: "+targetClass+"; targetObject: "+targetObject+"; targetMethod: "+targetMethod+"; staticMethod: "+staticMethod+"; arguments: "+arguments+";");
}
finally
{
logger.debug("end");
}
}
publicvoid setConcurrent(booleanconcurrent)
{
this.concurrent = concurrent;
}
publicvoid setDurable(booleandurable)
{
this.durable = durable;
}
publicvoid setGroup(String group)
{
this.group = group;
}
publicvoid setJobListenerNames(String[] jobListenerNames)
{
this.jobListenerNames = jobListenerNames;
}
publicvoid setShouldRecover(booleanshouldRecover)
{
this.shouldRecover = shouldRecover;
}
publicvoid setVolatility(booleanvolatility)
{
this.volatility = volatility;
}
publicstatic class MethodInvokingJob implements Job
{
protectedLog logger = LogFactory.getLog(getClass());
publicvoid execute(JobExecutionContext context)throws JobExecutionException
{
try
{
logger.debug("start");
String targetClass = context.getMergedJobDataMap().getString("targetClass");
Class targetClassClass =null;
if(targetClass!=null)
{
targetClassClass = Class.forName(targetClass);// Could throw ClassNotFoundException
}
Object targetObject = context.getMergedJobDataMap().get("targetObject");
if(targetObjectinstanceof BootstrapJob){
ApplicationContext ac = (ApplicationContext)context.getScheduler().getContext().get("applicationContext");
BootstrapJob target = (BootstrapJob)targetObject ;
target.execute(ac);
}else{
String targetMethod = context.getMergedJobDataMap().getString("targetMethod");
String staticMethod = context.getMergedJobDataMap().getString("staticMethod");
Object[] arguments = (Object[])context.getMergedJobDataMap().get("arguments");
MethodInvoker methodInvoker =new MethodInvoker();
methodInvoker.setTargetClass(targetClassClass);
methodInvoker.setTargetObject(targetObject);
methodInvoker.setTargetMethod(targetMethod);
methodInvoker.setStaticMethod(staticMethod);
methodInvoker.setArguments(arguments);
methodInvoker.prepare();
methodInvoker.invoke();
}
}
catch(Exception e)
{
thrownew JobExecutionException(e);
}
finally
{
logger.debug("end");
}
}
}</pre>
publicstatic class StatefulMethodInvokingJob extends MethodInvokingJob implements StatefulJob
{
// No additional functionality; just needs to implement StatefulJob.
}
  
publicObject[] getArguments()
{
returnarguments;
}
  
publicvoid setArguments(Object[] arguments)
{
this.arguments = arguments;
}
  
publicString getStaticMethod()
{
returnstaticMethod;
}
  
publicvoid setStaticMethod(String staticMethod)
{
this.staticMethod = staticMethod;
}
  
publicvoid setTargetObject(Object targetObject)
{
this.targetObject = targetObject;
}
}

实际的任务调度类:TaskDemo.java

1
2
3
4
5
public class TaskDemo implementsJob {
public void execute(){
System.out.println("这个是实际的任务");
}
}

Spring applicationContext.xml 配置

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<beanname="taskJob"class="TaskDemo"/>
 <beanid="_taskJob"class="PseudoJob">
<propertyname="targetJob"value="taskJob"/>
</bean>
<!--这里的MethodInvokingJobDetailFactoryBean路径要写自己重写的那个,非Spring包下的-->
<beanid="taskJobDetail"class="MethodInvokingJobDetailFactoryBean">
<propertyname="concurrent"value="true"/>
<propertyname="targetObject"ref="_taskJob"/>
<propertyname="targetMethod"value="execute"/>
</bean>
 <beanid="cronTrigger"class="org.springframework.scheduling.quartz.CronTriggerBean">
<propertyname="jobDetail">
<refbean="taskJobDetail"/>
</property>
<propertyname="cronExpression">
<value>0 0 6 * * ?</value>
</property>
</bean>
<!--dataSource采用spring中已有的dataSource-->
<beanclass="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<propertyname="configLocation"value="classpath:quartz.properties"/>
<propertyname="dataSource"ref="dataSource"/>
<propertyname="triggers">
<list>
<refbean="cronTrigger"/>
</list>
</property>
  <!-- 这个属性决定当在spring配置文件里修改任务执行时间的时候,是否更新数据库,默认是不更新的,也就是说,你第一通过修改配置文件定义了时间,之后再修改是不会起任何作用的-->
<propertyname="overwriteExistingJobs"value="true"/>
<propertyname="applicationContextSchedulerContextKey"value="applicationContext"/>
</bean>

OK,这样就大功告成了,别说还真神奇,经测试两台机器只有随机的一台执行定时任务.这篇文章只写了spring整合Quartz集群下的spring的配置,没有写Quartz的配置,有时间再整理一下.
Ps:好记性不如烂笔头儿,这技术的东西还是记载下来好.不过也真挺累啊.

原创粉丝点击