quartz集群搭建(整合spring)

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

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

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

我的配置见代码:

#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName=schedules
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.skipUpdateCheck=true
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=5
org.quartz.threadPool.threadPriority=5
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold=60000
org.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.StdJDBCDelegate
org.quartz.jobStore.dataSource=myDS
org.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.Driver
org.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=5
org.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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值