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的原生支持,但是报错. 异常信息:
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
据说是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
</pre>
public
class
BootstrapJob
implements
Serializable{
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接口
public
interface
Job
extends
Serializable{
void
execute();
}
MethodInvokingJobDetailFactoryBean
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
implements
FactoryBean, BeanNameAware, InitializingBean
{
private
Log logger = LogFactory.getLog(getClass());
private
String group = Scheduler.DEFAULT_GROUP;
private
boolean
concurrent =
true
;
private
boolean
durable =
false
;
private
boolean
volatility =
false
;
private
boolean
shouldRecover =
false
;
private
String[] jobListenerNames;
private
String beanName;
private
JobDetail jobDetail;
private
String targetClass;
private
Object targetObject;
private
String targetMethod;
private
String staticMethod;
private
Object[] 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()
throws
Exception
{
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"
);
}
}
public
void
setConcurrent(
boolean
concurrent)
{
this
.concurrent = concurrent;
}
public
void
setDurable(
boolean
durable)
{
this
.durable = durable;
}
public
void
setGroup(String group)
{
this
.group = group;
}
public
void
setJobListenerNames(String[] jobListenerNames)
{
this
.jobListenerNames = jobListenerNames;
}
public
void
setShouldRecover(
boolean
shouldRecover)
{
this
.shouldRecover = shouldRecover;
}
public
void
setVolatility(
boolean
volatility)
{
this
.volatility = volatility;
}
public
static
class
MethodInvokingJob
implements
Job
{
protected
Log logger = LogFactory.getLog(getClass());
public
void
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
(targetObject
instanceof
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)
{
throw
new
JobExecutionException(e);
}
finally
{
logger.debug(
"end"
);
}
}
}</pre>
public
static
class
StatefulMethodInvokingJob
extends
MethodInvokingJob
implements
StatefulJob
{
// No additional functionality; just needs to implement StatefulJob.
}
public
Object[] getArguments()
{
return
arguments;
}
public
void
setArguments(Object[] arguments)
{
this
.arguments = arguments;
}
public
String getStaticMethod()
{
return
staticMethod;
}
public
void
setStaticMethod(String staticMethod)
{
this
.staticMethod = staticMethod;
}
public
void
setTargetObject(Object targetObject)
{
this
.targetObject = targetObject;
}
}
实际的任务调度类:TaskDemo.java
public
class
TaskDemo
implements
Job {
public
void
execute(){
System.out.println(
"这个是实际的任务"
);
}
}
Spring applicationContext.xml 配置
<
bean
name
=
"taskJob"
class
=
"TaskDemo"
/>
<
bean
id
=
"_taskJob"
class
=
"PseudoJob"
>
<
property
name
=
"targetJob"
value
=
"taskJob"
/>
</
bean
>
<!--这里的MethodInvokingJobDetailFactoryBean路径要写自己重写的那个,非Spring包下的-->
<
bean
id
=
"taskJobDetail"
class
=
"MethodInvokingJobDetailFactoryBean"
>
<
property
name
=
"concurrent"
value
=
"true"
/>
<
property
name
=
"targetObject"
ref
=
"_taskJob"
/>
<
property
name
=
"targetMethod"
value
=
"execute"
/>
</
bean
>
<
bean
id
=
"cronTrigger"
class
=
"org.springframework.scheduling.quartz.CronTriggerBean"
>
<
property
name
=
"jobDetail"
>
<
ref
bean
=
"taskJobDetail"
/>
</
property
>
<
property
name
=
"cronExpression"
>
<
value
>0 0 6 * * ?</
value
>
</
property
>
</
bean
>
<!--dataSource采用spring中已有的dataSource-->
<
bean
class
=
"org.springframework.scheduling.quartz.SchedulerFactoryBean"
>
<
property
name
=
"configLocation"
value
=
"classpath:quartz.properties"
/>
<
property
name
=
"dataSource"
ref
=
"dataSource"
/>
<
property
name
=
"triggers"
>
<
list
>
<
ref
bean
=
"cronTrigger"
/>
</
list
>
</
property
>
<!-- 这个属性决定当在spring配置文件里修改任务执行时间的时候,是否更新数据库,默认是不更新的,也就是说,你第一通过修改配置文件定义了时间,之后再修改是不会起任何作用的-->
<
property
name
=
"overwriteExistingJobs"
value
=
"true"
/>
<
property
name
=
"applicationContextSchedulerContextKey"
value
=
"applicationContext"
/>
</
bean
>
OK,这样就大功告成了,别说还真神奇,经测试两台机器只有随机的一台执行定时任务.这篇文章只写了spring整合Quartz集群下的spring的配置,没有写Quartz的配置,有时间再整理一下.
Ps:好记性不如烂笔头儿,这技术的东西还是记载下来好.不过也真挺累啊.
- Spring+Quartz的集群配置
- Quartz+Spring的集群配置
- Spring+quartz集群配置
- spring quartz 集群配置
- spring quartz集群配置
- Spring+Quartz的集群配置问题
- quartz集成spring下的集群配置
- spring集成quartz的集群配置实例
- Spring中定时器Quartz 集群配置
- spring quartz集群 动态配置任务
- quartz集群配置+spring(一)
- 浅析Quartz的集群配置
- 浅析Quartz的集群配置
- 浅析Quartz的集群配置
- spring的Quartz配置
- Spring+quartz集群配置,Spring定时任务集群,quartz定时任务集群
- Spring之——quartz的配置方式(集群与不集群)
- spring 集成quartz 用数据库实现quartz的集群
- Mysql数据库应用中简单的封装
- 代码检查错误列表总结
- python学习六:异常处理、map/reduce/filter内置函数、python集合(set)类型、元组转列表
- Maximum Subarray
- 线程
- Spring+Quartz的集群配置
- 困难重重,终于在UBUNTU12.04 下对于JAVA 安装成功 关键词:update
- [LeetCode] Binary Tree Level Order Traversal
- .Net类库预定义委托之Predicate
- 快速掌握23种设计模式(状态,策略,观察者)
- Kmedoids算法Java实现(可处理噪声)
- c++纯虚函数
- Android访问WebService提示错误
- c++中const详细解释