quartz集群搭建(整合spring)

来源:互联网 发布:极限挑战知乎话题 编辑:程序博客网 时间:2024/06/05 19:32

本文记录搭建quartz集群的过程和遇到的各种坑:

搭建又单节点到集群搭建非常简单,步骤以官网为准

主要内容就是添加了quartz.properties 文件,并修改org.quartz.jobStore.class属性(集群搭建依赖于数据库,不能再用RAMStore)

我的配置见代码:

#============================================================================# Configure Main Scheduler Properties#============================================================================org.quartz.scheduler.instanceName=schedulesorg.quartz.scheduler.instanceId=AUTOorg.quartz.scheduler.skipUpdateCheck=true#============================================================================# Configure ThreadPool#============================================================================org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount=5org.quartz.threadPool.threadPriority=5#============================================================================# Configure JobStore#============================================================================org.quartz.jobStore.misfireThreshold=60000org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX##org.quartz.jobStore.class属性为 JobStoreTX,将任务持久化到数据中。   ##因为集群中节点依赖于数据库来传播 Scheduler 实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。   ##这意味着你必须使用 JobStoreTX 或是 JobStoreCMT 作为 Job 存储;你不能在集群中使用 RAMJobStore。   org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegateorg.quartz.jobStore.dataSource=myDSorg.quartz.jobStore.tablePrefix=QRTZ_org.quartz.jobStore.isClustered=true##org.quartz.jobStore.clusterCheckinInterval 属性定义了Scheduler 实例检入到数据库中的频率(单位:毫秒)。   ##Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。   ##通过检入操作,Scheduler 也会更新自身的状态记录。clusterChedkinInterval 越小,Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)。   org.quartz.jobStore.clusterCheckinInterval = 20000#============================================================================# Configure Datasources#============================================================================org.quartz.dataSource.myDS.driver=com.mysql.jdbc.Driverorg.quartz.dataSource.myDS.URL=@QUARTZ_DB_URL@org.quartz.dataSource.myDS.user=@QUARTZ_DB_USER@org.quartz.dataSource.myDS.password=@QUARTZ_DB_PASSWORD@org.quartz.dataSource.myDS.maxConnections=5org.quartz.dataSource.myDS.validationQuery=select 0


在scheduler.xml中添加标红的内容,支出quartz配置文件的位置。scheduler.xml的其他内容下文再详解

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"><beans>    <bean id="tarzanDumpTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">        <property name="jobDetail">            <bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">                <property name="durability" value="true" />                <property name="requestsRecovery" value="true" />                <property name="name" value="tarzanDump"/>                <property name="jobClass">                    <value>com.my.scheduler.MyDetailQuartzJobBean</value>                </property>                <property name="jobDataAsMap">                    <map>                        <entry key="targetObject" value="tarzanDump" />                        <entry key="targetMethod" value="execute" />                    </map>                </property>            </bean>        </property>        <property name="cronExpression" value="0 0 3 * * ? *"></property>    </bean>    <bean id="cacheJamEnrollNumTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">        <property name="jobDetail">            <bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">                <property name="durability" value="true" />                <property name="requestsRecovery" value="true" />                <property name="name" value="cacheJamEnrollNum"/>                <property name="jobClass">                    <value>com.my.scheduler.MyDetailQuartzJobBean</value>                </property>                <property name="jobDataAsMap">                    <map>                        <entry key="targetObject" value="cacheJamEnrollNum" />                        <entry key="targetMethod" value="execute" />                    </map>                </property>            </bean>        </property>        <property name="cronExpression" value="0 0/10 * * * ? * "/>    </bean>    <bean id="SpringJobSchedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">        <property name="triggers">            <list>               <ref bean="tarzanDumpTrigger"/>               <ref bean="cacheJamEnrollNumTrigger"/>            </list>        </property>        <property name="applicationContextSchedulerContextKey" value="applicationContext" />        <property name="configLocation" value="./WEB-INF/quartz.properties"/>    </bean></beans>


可以看到,跟单节点配置几乎没有太多的区别。但是你会发现,里面的坑很多。一一说起

spring创建JobDetail的定时任务有两种方式:

第一种方式是利用Spring封装的Quartz类进行特定方法的实现,第二种是通过透明的使用Quartz达到定时任务开发的目的,总体说第二种对开发人员更方便!

配置Spring的任务调度抽象层简化了任务调度,在Quartz的基础上提供了更好的调度对象。Spring使用Quartz框架来完成任务调度,创建Quartz的作业Bean(JobDetail),有一下两种方法:

   1:利用JobDetailBean包装QuartzJobBean子类(即Job类)的实例。

   2:利用MethodInvokingJobDetailFactoryBean工厂Bean包装普通的Java对象(即Job类)。

   说明:

      1:采用第一种方法 创建job类,一定要继承QuartzJobBean ,实现 executeInternal(JobExecutionContextjobexecutioncontext)方法,此方法就是被调度任务的执行体,然后将此Job类的实例直接配置到JobDetailBean中即可。这种方法和在普通的Quartz编程中是一样的。

      2:采用第二种方法 创建Job类,无须继承父类,直接配置MethodInvokingJobDetailFactoryBean即可。但需要指定一下两个属性:

        targetObject:指定包含任务执行体的Bean实例。

        targetMethod:指定将指定Bean实例的该方法包装成任务的执行体。

第二种情况下,因为spring的methodInvoker不可序列化,没有办法在持久化到数据库中,被我先抛弃了(网上也是有解决办法的,我没有仔细去看,采用了第一种方式)

我们可以这样配置:

<bean id="cronTriggerPunch"       class="org.springframework.scheduling.quartz.CronTriggerBean">      <property name="jobDetail">          <!-- 使用嵌套Bean的方式来定义任务Bean -->          <bean class="org.springframework.scheduling.quartz.JobDetailBean">              <!-- 指定任务Bean的实现类 -->              <property name="jobClass" value="com.my.MyJob"/><!-- MyJob需要集成QuartzJobBean -->      </property>      <!-- 指定Cron表达式:周一到周五7点、12点执行调度 -->      <property name="cronExpression"           value="0 0 7,12 ? * MON-FRI"/>  </bean>  
这种情况下,JobDetailBean里必须要指定jobClass这个属性。

可能有这样两种情况,

1.我们的一个其他组件需要用到一个MyJob的实例,这种情况下,我们可能期望把MyJob交给spring来管理,而在JobDetailBean中只是指向spring中的这个bean就好了

        2.MyJob中的某些属性不能序列化。

 如果只是第一种情况的话,可以用封装来解决问题。第二个问题就不是那么好办了。有些属性是真的没有办法被实例化或者代价很大。


下面介绍种解决方法:

自己定义一个MyJobBean继承QuartzJobBean

public class MyJobBean extends QuartzJobBean{    private static final Logger LOG = Logger.getLogger(MyJobBean.class);    private String targetObject;    private String targetMethod;    private ApplicationContext ctx;    @Override    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {        try {            Object otargetObject = ctx.getBean(targetObject);            Method m = null;            try {                m = otargetObject.getClass().getDeclaredMethod(targetMethod);                m.invoke(otargetObject);            } catch (SecurityException e) {                LOG.error(e.getMessage(),e);            } catch (NoSuchMethodException e) {                LOG.error(e.getMessage(),e);            }        } catch (Exception e) {            throw new JobExecutionException(e);        }    }    public void setApplicationContext(ApplicationContext applicationContext) {        this.ctx = applicationContext;    }    public void setTargetObject(String targetObject) {        this.targetObject = targetObject;    }    public void setTargetMethod(String targetMethod) {        this.targetMethod = targetMethod;    }}


在scheduler.xml里这么配:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"><beans>    <bean id="tarzanDump" class="com.my.TarzanDump"/>    <bean id="tarzanDumpTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">        <property name="jobDetail">            <bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">                <property name="durability" value="true" />                <property name="requestsRecovery" value="true" />                <property name="name" value="tarzanDump"/>                <property name="jobClass">                    <value>com.my.scheduler.MyDetailQuartzJobBean</value>                </property>                <property name="jobDataAsMap">                    <map>                        <entry key="targetObject" value="tarzanDump" />                        <entry key="targetMethod" value="execute" />                    </map>                </property>            </bean>        </property>        <property name="cronExpression" value="0 0 3 * * ? *"></property>    </bean>    <bean id="cacheJamEnrollNumTrigger" class="com.my.cacheJamEnrollNumTrigger"/>    <bean id="cacheJamEnrollNumTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">        <property name="jobDetail">            <bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">                <property name="durability" value="true" />                <property name="requestsRecovery" value="true" />                <property name="name" value="cacheJamEnrollNum"/>                <property name="jobClass">                    <value>com.my.scheduler.MyDetailQuartzJobBean</value>                </property>                <property name="jobDataAsMap">                    <map>                        <entry key="targetObject" value="cacheJamEnrollNum" />                        <entry key="targetMethod" value="execute" />                    </map>                </property>            </bean>        </property>        <property name="cronExpression" value="0 0/10 * * * ? * "/>    </bean>    <bean id="SpringJobSchedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">        <property name="triggers">            <list>               <ref bean="tarzanDumpTrigger"/>               <ref bean="cacheJamEnrollNumTrigger"/>            </list>        </property>        <property name="applicationContextSchedulerContextKey" value="applicationContext" />        <property name="configLocation" value="./WEB-INF/quartz.properties"/>    </bean></beans>


这种情况下tarzanDum和cacheJamEnrollNum这些bean都是在spring中管理,在MyJobBean从spring中获取到这些bean,并反射出来调用在scheduler.xml里指定的方法。可以解决上述的两个难题。

但是你会发现,数据库里的jobDetail序列化的实际上只是MyJobBean这个类的实例,并不涉及真正的job(tarzanDump和cacheJamEnrollNum)。当quartz节点从数据库里取到MyJobBean的时候,再向spring中请求获得相应的bean和方法。









0 0
原创粉丝点击