一般系统随着用户量的增长,慢慢的都会由单机走向集群,而很多时候我们又需要跑一些定时任务,Quartz就是为此而生,那么单机好办,集群中的Quartz又该如何配置呢?集群中的Quartz各节点之间是通过同一个数据库实例(准确的说是同一个数据库实例的同一套表)来感知彼此的,既然是通过数据库,那么就先看看数SQL文件

  1. SQL文件
#  
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar  
#  
# In your Quartz properties file, you'll need to set  
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate  
#
DROP TABLE IF EXISTS QRTZ_JOB_LISTENERS;  
DROP TABLE IF EXISTS QRTZ_TRIGGER_LISTENERS;  
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;  
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;  
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;  
DROP TABLE IF EXISTS QRTZ_LOCKS;  
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;  
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;  
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;  
DROP TABLE IF EXISTS QRTZ_TRIGGERS;  
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;  
DROP TABLE IF EXISTS QRTZ_CALENDARS;

CREATE TABLE QRTZ_JOB_DETAILS (  
  JOB_NAME VARCHAR(200) NOT NULL,  
  JOB_GROUP VARCHAR(200) NOT NULL,  
  DESCRIPTION VARCHAR(250) NULL,  
  JOB_CLASS_NAME VARCHAR(250) NOT NULL,  
  IS_DURABLE VARCHAR(1) NOT NULL,  
  IS_VOLATILE VARCHAR(1) NOT NULL,  
  IS_STATEFUL VARCHAR(1) NOT NULL,  
  REQUESTS_RECOVERY VARCHAR(1) NOT NULL,  
  JOB_DATA BLOB NULL,  
  PRIMARY KEY (JOB_NAME,JOB_GROUP)  
);

CREATE TABLE QRTZ_JOB_LISTENERS (  
  JOB_NAME VARCHAR(200) NOT NULL,  
  JOB_GROUP VARCHAR(200) NOT NULL,  
  JOB_LISTENER VARCHAR(200) NOT NULL,  
  PRIMARY KEY (JOB_NAME,JOB_GROUP,JOB_LISTENER),  
  FOREIGN KEY (JOB_NAME,JOB_GROUP),
  REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)  
);

CREATE TABLE QRTZ_TRIGGERS (
  TRIGGER_NAME VARCHAR(200) NOT NULL,  
  TRIGGER_GROUP VARCHAR(200) NOT NULL,  
  JOB_NAME VARCHAR(200) NOT NULL,  
  JOB_GROUP VARCHAR(200) NOT NULL,  
  IS_VOLATILE VARCHAR(1) NOT NULL,  
  DESCRIPTION VARCHAR(250) NULL,  
  NEXT_FIRE_TIME BIGINT(13) NULL,  
  PREV_FIRE_TIME BIGINT(13) NULL,  
  PRIORITY INTEGER NULL,  
  TRIGGER_STATE VARCHAR(16) NOT NULL,  
  TRIGGER_TYPE VARCHAR(8) NOT NULL,  
  START_TIME BIGINT(13) NOT NULL,  
  END_TIME BIGINT(13) NULL,  
  CALENDAR_NAME VARCHAR(200) NULL,  
  MISFIRE_INSTR SMALLINT(2) NULL,  
  JOB_DATA BLOB NULL,  
  PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),  
  FOREIGN KEY (JOB_NAME,JOB_GROUP),
  REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)  
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
  TRIGGER_NAME VARCHAR(200) NOT NULL,  
  TRIGGER_GROUP VARCHAR(200) NOT NULL,  
  REPEAT_COUNT BIGINT(7) NOT NULL,  
  REPEAT_INTERVAL BIGINT(12) NOT NULL,  
  TIMES_TRIGGERED BIGINT(10) NOT NULL,  
  PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),  
  FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP),
  REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP) 
);

CREATE TABLE QRTZ_CRON_TRIGGERS (  
  TRIGGER_NAME VARCHAR(200) NOT NULL,  
  TRIGGER_GROUP VARCHAR(200) NOT NULL,  
  CRON_EXPRESSION VARCHAR(200) NOT NULL,  
  TIME_ZONE_ID VARCHAR(80),  
  PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),  
  FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP),
  REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP) 
);

CREATE TABLE QRTZ_BLOB_TRIGGERS (
  TRIGGER_NAME VARCHAR(200) NOT NULL,  
  TRIGGER_GROUP VARCHAR(200) NOT NULL,  
  BLOB_DATA BLOB NULL,  
  PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),  
  FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)  
  REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)  
);

CREATE TABLE QRTZ_TRIGGER_LISTENERS (  
  TRIGGER_NAME VARCHAR(200) NOT NULL,  
  TRIGGER_GROUP VARCHAR(200) NOT NULL,  
  TRIGGER_LISTENER VARCHAR(200) NOT NULL,  
  PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_LISTENER),  
  FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)  
  REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CALENDARS (  
  CALENDAR_NAME VARCHAR(200) NOT NULL,  
  CALENDAR BLOB NOT NULL,  
  PRIMARY KEY (CALENDAR_NAME)  
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (  
  TRIGGER_GROUP VARCHAR(200) NOT NULL,  
  PRIMARY KEY (TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS (  
  ENTRY_ID VARCHAR(95) NOT NULL,  
  TRIGGER_NAME VARCHAR(200) NOT NULL,  
  TRIGGER_GROUP VARCHAR(200) NOT NULL,  
  IS_VOLATILE VARCHAR(1) NOT NULL,  
  INSTANCE_NAME VARCHAR(200) NOT NULL,  
  FIRED_TIME BIGINT(13) NOT NULL,  
  PRIORITY INTEGER NOT NULL,  
  STATE VARCHAR(16) NOT NULL,  
  JOB_NAME VARCHAR(200) NULL,  
  JOB_GROUP VARCHAR(200) NULL,  
  IS_STATEFUL VARCHAR(1) NULL,  
  REQUESTS_RECOVERY VARCHAR(1) NULL,  
  PRIMARY KEY (ENTRY_ID)  
);

CREATE TABLE QRTZ_SCHEDULER_STATE (  
  INSTANCE_NAME VARCHAR(200) NOT NULL,  
  LAST_CHECKIN_TIME BIGINT(13) NOT NULL,  
  CHECKIN_INTERVAL BIGINT(13) NOT NULL,  
  PRIMARY KEY (INSTANCE_NAME)  
);

CREATE TABLE QRTZ_LOCKS (  
  LOCK_NAME VARCHAR(40) NOT NULL,  
  PRIMARY KEY (LOCK_NAME)  
);

INSERT INTO QRTZ_LOCKS values('TRIGGER_ACCESS');  
INSERT INTO QRTZ_LOCKS values('JOB_ACCESS');  
INSERT INTO QRTZ_LOCKS values('CALENDAR_ACCESS');  
INSERT INTO QRTZ_LOCKS values('STATE_ACCESS');  
INSERT INTO QRTZ_LOCKS values('MISFIRE_ACCESS');

commit;

其实这个文件在Quartz的文档中是可以找到的,这里贴出来只是为了大家方便,建好数据库之后,接下来肯定就是看看数据源的问题了

  1. quartz.properties
#==============================================================  
#Configure Main Scheduler Properties  
#==============================================================  
org.quartz.scheduler.instanceName = quartzScheduler  
org.quartz.scheduler.instanceId = AUTO

#==============================================================  
#Configure JobStore  
#==============================================================  
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.isClustered = true  
org.quartz.jobStore.clusterCheckinInterval = 60000  
org.quartz.jobStore.dataSource = myDS

#==============================================================  
#Configure DataSource  
#==============================================================  
org.quartz.dataSource.myDS.driver =  
org.quartz.dataSource.myDS.URL =  
org.quartz.dataSource.myDS.user =  
org.quartz.dataSource.myDS.password =  
org.quartz.dataSource.myDS.maxConnections = 30

#==============================================================  
#Configure ThreadPool  
#==============================================================  
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool  
org.quartz.threadPool.threadCount = 10  
org.quartz.threadPool.threadPriority = 5  
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

把这个properties文件放到classpath下,那么他就会覆盖掉quartz的jar中原有的配置文件,数据源的信息读者根据自己的情况填写。
在这个配置文件中,除了数据源、线程池等配置外,第四行和第五行我们指定了一个scheduler实例,实例ID为自动分配,第十三十四行指定了集群相应配置,检查间隔为60s,也就是一分钟。

  1. 和spring集成的配置文件
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
  xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"  
  xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"  
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="  
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

  <!-- 调度工厂 -->  
  <!-- 注意:更改配置时必须初始化数据库中的所有数据 -->  
  <bean id="mapScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false" autowire="no" >  
    <property name="triggers">  
      <list>  
        <ref bean ="quartzTask"/>  
      </list>  
    </property>  
    <property name="applicationContextSchedulerContextKey" value="applicationContext" />  
    <property name="configLocation" value="classpath:quartz.properties" />  
  </bean>

  <bean id="quartzTask" class="org.springframework.scheduling.quartz.CronTriggerBean">  
    <property name="jobDetail">  
      <ref bean="jobDetail" />  
    </property>  
    <property name="cronExpression" value="0 0/5 * * * ?"></property>
  </bean>  
  <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">  
    <property name="jobClass">  
      <value>cn.bridgeli.quartz.MyDetailQuartzJobBean</value>  
    </property>  
    <property name="jobDataAsMap">  
      <map>  
        <entry key="targetObject" value="cacheRefreshQuartz" />  
        <entry key="targetMethod" value="execute" />  
      </map>  
    </property>
  </bean>  
  <bean id="cacheRefreshQuartz" class="cn.bridgeli.task.QuartzImpl"/>  
</beans>

由于MethodInvokingJobDetailFactoryBean 类中的 methodInvoking 方法,是不支持序列化的,因此在把 QUARTZ 的 TASK 序列化进入数据库时就会抛错,所以我们要自己实现MethodInvokingJobDetailFactoryBean 的功能,也就是第三十三行我们要用自己的MyDetailQuartzJobBean 去替换。

  1. MyDetailQuartzJobBean
package cn.bridgeli.quartz;

import org.quartz.JobExecutionContext;  
import org.quartz.JobExecutionException;  
import org.springframework.context.ApplicationContext;  
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.lang.reflect.Method;

public class MyDetailQuartzJobBean extends QuartzJobBean {  
  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().getMethod(targetMethod, new Class[]{});
        m.invoke(otargetObject, new Object[]{});  
      } catch (SecurityException e) {  
        e.printStackTrace();  
      } catch (NoSuchMethodException e) {  
        e.printStackTrace();  
      }  
    } 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;  
  }
}

终于完成了,接下来我们就可以在集群中随心所欲的使用Quartz了。

参考资料:http://forhope.iteye.com/blog/1398990