spring quartz 集群配置

标签: spring quartz 集群 | 发表时间:2015-03-07 15:08 | 作者:liuchangqing123
出处:http://blog.csdn.net

Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。


在项目中有大量的后台任务需要调度执行,如构建索引、统计报表、周期同步数据等等,要求任务调度系统具备高可用性、负载均衡特性,使用Quartz 会很方便。

下文是spring和quartz进行整合,同时支持集群部署。quartz集群的支持是通过数据库进行任务调度的感知。

1.使用的版本情况:spring 3.1.2 + quartz 1.8.6

2.数据库导入quartz使用的table,在quartz的源码压缩包中可以找到相应的sql文件,导入即可。


3.配置数据库连接池

使用spring中配好的datasource即可。

4.配置quartz.properties

##Quartz 调度任务所需的配置文件  
##org.quartz.scheduler.instanceName属性可为任何值,用在 JDBC JobStore 中来唯一标识实例,但是所有集群节点中必须相同。
org.quartz.scheduler.instanceName = instanceScheduler         
##org.quartz.scheduler.instanceId 属性为 AUTO即可,基于主机名和时间戳来产生实例 ID。 
org.quartz.scheduler.instanceId = AUTO         
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool        
org.quartz.threadPool.threadCount = 10       
org.quartz.threadPool.threadPriority = 5       
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true      
org.quartz.jobStore.misfireThreshold = 60000      
##org.quartz.jobStore.class属性为 JobStoreTX,将任务持久化到数据中。   
##因为集群中节点依赖于数据库来传播 Scheduler 实例的状态,你只能在使用 JDBC JobStore 时应用Quartz 集群。   
##这意味着你必须使用 JobStoreTX 或是 JobStoreCMT 作为 Job 存储;你不能在集群中使用 RAMJobStore。   
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX        
org.quartz.jobStore.driverDelegateClass= org.quartz.impl.jdbcjobstore.StdJDBCDelegate  
org.quartz.jobStore.tablePrefix = QRTZ_     
org.quartz.jobStore.maxMisfiresToHandleAtATime= 10       
##org.quartz.jobStore.isClustered 属性为 true,你就告诉了 Scheduler 实例要它参与到一个集群当中。   
##这一属性会贯穿于调度框架的始终,用于修改集群环境中操作的默认行为。    
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
5.配置timerTask.xml

<?xml version="1.0" encoding= "UTF-8"?>
    <!--
         字段名(项) 必须 值范围 特殊字符 秒 是 0-59 , - * / 分 是 0-59 , - * / 时 是 0-23 , - * /
         月的某天 是 1-31 , - * ? / L W 月 是 1-12 or JAN-DEC , - * / 星期的某天 是 1-7 or
         SUN-SAT , - * ? / L # 年 否 empty, 1970-2099 , - * /
    -->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
<beans>
    <bean id="secondProcessor" class="com.xxx.framework.back.SecondProcessor" />
    <bean id="minuteProcessor" class="com.xxx.framework.back.MinuteProcessor" />
    <bean id="minutesJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean" >
          <property name="jobClass"
              value="com.xxx.framework.timerTask.MinuteQuartzJob" />
          <property name="jobDataAsMap" >
              <map>
                  <entry key="taskExecuters" >
                       <list>
                          <value>minuteProcessor</value> 
                       </list>
                  </entry>
                  <entry key="isWorking" value="true" /><!-- 是否开启分钟级任务 -->
              </map>
          </property>
    </bean >
    <bean id="secondsJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean" >
          <property name="jobClass"
             value="com.xxx.framework.timerTask.SecondQuartzJob" />
          <property name="jobDataAsMap" >
              <map>
                  <entry key="taskExecuters" >
                       <list>
                           <!-- <value>secondProcessor</value>  -->
                       </list>
                  </entry>
                  <entry key="isWorking" value="true" /><!-- 是否开启秒级任务 -->
              </map>
          </property>
    </bean >
    <bean id="minutesTaskScheduler" class="org.springframework.scheduling.quartz.CronTriggerBean" >
          <property name="jobDetail" ref="minutesJobDetail" />
          <property name="cronExpression" value="${background.minuteExecute}" />
    </bean >
    <bean id="secondsTaskScheduler" class="org.springframework.scheduling.quartz.CronTriggerBean" >
          <property name="jobDetail" ref="secondsJobDetail" />
          <property name="cronExpression" value="${background.secondExecute}" />
    </bean >

    <bean id="timeTask"
         class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
          destroy-method="destroy" >
          <property name="dataSource" >
              <ref bean="source" />  <!--数据源引用指向,包含集群所需的所有表-->
          </property>
          <property name="transactionManager" ref="transactionManager" />
          <property name="overwriteExistingJobs" value="true" />
          <property name="autoStartup" value="true" />
          <property name="applicationContextSchedulerContextKey" value="applicationContext" />
          <property name="configLocation" value="classpath:quartz.properties" />
          <!--configLocation:用于指明quartz的配置文件的位置 -->
          <property name="triggers" >
              <list>
                  <ref bean="secondsTaskScheduler" /><!-- 秒级任务调用 -->
                  <ref bean="minutesTaskScheduler" /><!-- 分钟级任务调用 -->
              </list>
          </property>
    </bean >

</beans>
相关的timerTask.properties文件

background.secondExecute=0/1 * * * * ?
background.minuteExecute=0 0/1 * * * ?
background.hourExecute=0 0 0/1 * * ?
background.dayStartExecute=59 59 23 * * ?
background.dayExecute=23 23 4 * * ?
background.weekStartExecute=0 0 0 ? * MON

6.编写相关的类

1)编写接口IBatchProcessor

public interface IBatchProcessor {
    void execute( int times);
}


2)实现类MinuteProcessor

public class MinuteProcessor implements IBatchProcessor{

    @Override
    public void execute(int times) {
         System. out.println("MinuteProcessor times : " + times);
    }

}

3)配置job任务两种方式:继承QuartzJobBean的类,重写executeInternal();用org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean指定类和方法。

第一种方式:

public class MinuteQuartzJob extends QuartzJobBean {
    private Logger logger = Logger.getLogger(getClass());
    private List<String> taskExecuters;

    /**
     * 分钟级计数器
     */
    private static int minuterCounter = 1;
    private static long lastMinuterTaskTime = -1;
    private boolean isWorking ;

    private Lock minuteLock = new ReentrantLock();

    public List<String> getTaskExecuters() {
          return taskExecuters ;
    }

    public void setTaskExecuters(List<String> taskExecuters) {
          this.taskExecuters = taskExecuters;
    }

    protected void executeInternal(JobExecutionContext jobCtx)
              throws JobExecutionException {
          if (!isWorking )
              return;
          if (minuteLock .tryLock()) {
              try {
                 SchedulerContext schedCtx = jobCtx.getScheduler().getContext();
                 ApplicationContext appCtx = (ApplicationContext) schedCtx
                          .get( "applicationContext");
                  long currTime = System.currentTimeMillis();
                  if (lastMinuterTaskTime != -1) {
                       // 间隔不到55秒秒就触发了一次新的分钟级任务,不执行
                       if (currTime - lastMinuterTaskTime < 55000) {
                           logger.warn("分钟级任务触发时间有问题" );
                           return;
                      }
                 }
                  lastMinuterTaskTime = currTime;
                  for (String taskItem : taskExecuters) {
                      IBatchProcessor proc = (IBatchProcessor) appCtx
                               .getBean(taskItem);
                      proc.execute( minuterCounter);
                 }
                  minuterCounter++;
             } catch (Exception ex) {
                 ex.printStackTrace();
             } finally {
                  minuteLock.unlock();
             }

         } else {
              logger.warn("无法获得分钟级后台任务锁!!!!!!" );
         }
    }

    public boolean getIsWorking() {
          return isWorking ;
    }

    public void setIsWorking(boolean isWorking) {
          this.isWorking = isWorking;
    }

}

第二种方式:


直接使用会报java.io.NotSerializableException异常。很恶心!哈哈,这种方式实现的话自己去查解决方法吧(重写MethodInvokingJobDetailFactoryBean)。


作者:liuchangqing123 发表于2015/3/7 22:51:32 原文链接
阅读:6 评论:0 查看评论

相关 [spring quartz 集群] 推荐:

spring quartz 集群配置

- - CSDN博客推荐文章
Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中. 它提供了巨大的灵活性而不牺牲简单性. 你能够用它来为执行一个作业而创建简单的或复杂的调度. 在项目中有大量的后台任务需要调度执行,如构建索引、统计报表、周期同步数据等等,要求任务调度系统具备高可用性、负载均衡特性,使用Quartz 会很方便.

quartz集群分布式(并发)部署解决方案-Spring

- - 企业架构 - ITeye博客
项目中使用分布式并发部署定时任务,多台跨JVM,按照常理逻辑每个JVM的定时任务会各自运行,这样就会存在问题,多台分布式JVM机器的应用服务同时干活,一个是加重服务负担,另外一个是存在严重的逻辑问题,. 比如需要回滚的数据,就回滚了多次,刚好quartz提供很好的解决方案. 集群分布式并发环境中使用QUARTZ定时任务调度,会在各个节点会上报任务,存到数据库中,执行时会从数据库中取出触发器来执行,如果触发器的名称和执行时间相同,则只有一个节点去执行此任务.

quartz spring 实现动态定时任务

- - 企业架构 - ITeye博客
在实际项目应用中经常会用到定时任务,可以通过quartz和spring的简单配置即可完成,但如果要改变任务的执行时间、频率,废弃任务等就需要改变配置甚至代码需要重启服务器,这里介绍一下如何通过quartz与spring的组合实现动态的改变定时任务的状态的一个实现. 参考文章: http://www.meiriyouke.net/?p=82.

Spring+quartz 实现动态管理任务

- - 寒江孤影
在实际项目应用中经常会用到定时任务,可以通过quartz和spring的简单配置即可完成,但如果要改变任务的执行时间、频率,废弃任务等就需要改变配置甚至代码需要重启服务器,这里介绍一下如何通过quartz与spring的组合实现动态的改变定时任务的状态的一个实现. 本文章适合对quartz和spring有一定了解的读者.

Quartz集群实战及原理解析

- - CSDN博客推荐文章
  选Quartz的团队基本上是冲着Quartz本身实现的集群去的, 不然JDK自带Timer就可以实现相同的功能, 而Timer存在的单点故障是生产环境上所不能容忍的. 在自己造个有负载均衡和支持集群(高可用、伸缩性)的调度框架又影响项目的进度, 所以大多数团队都直接使用了Quartz来作为调度框架.

Quartz应用与集群原理分析

- - 美团技术团队
美团CRM系统中每天有大量的后台任务需要调度执行,如构建索引、统计报表、周期同步数据等等,要求任务调度系统具备高可用性、负载均衡特性,可以管理并监控任务的执行流程,以保证任务的正确执行. 美团CRM系统的任务调度模块经历了以下历史方案. 每天晚上运行定时任务,通过SQL脚本+crontab方式执行,例如,.

Spring实现后台的任务调度TimerTask和Quartz

- - CSDN博客互联网推荐文章
最近整后台,涉及到两个后台调度的问题. 一是以时间间隔为条件的轮询调度;. 运用场景:每隔5分钟抓取数据;. 二是一某个时间点为条件的轮询调度;. 运用场景:后台日志货报表生成上传,每个周一生成上一周的,每个月初生成上一月. 其实按周来执行调度,用前面一个场景也可以实现,但是按月生成,因为每月时间不固定,必须动态判断和执行.

spring quartz学习总结: cluster的配置和示例

- - 开源软件 - ITeye博客
    在前面一篇 文章里我们讨论了quartz和spring quartz的基本流程以及配置,这里针对一些quartz应用的场景和配置重点解读一下quartz的cluster配置和应用.     在前面的执行quartz任务的环境里,我们都是在单独的一台机器上执行任务. 这种单点执行的方式存在着一些问题.

Spring 任务调度Quartz的cron表达式

- - ITeye博客
Spring支持基于Quartz的任务调度,那么其cron表达式类似于Linux的crontab,有7个字符构成,详情如下:. 表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五. 表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即等同于10,11,12.

Spring+Quartz实现动态添加定时任务

- - 编程语言 - ITeye博客
   0 0 0 * * ?. //如果全部定时任务都要动态生成,可以只配置这一个即可. * Description: 计时器工具类. private static Scheduler scheduler;// 调度器.   * Description: 启动一个自定义的job.