spring3+quartz1集群配置、分布式集群配置

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/quguang65265/article/details/67069006

spring3+quartz1集群配置、分布式集群配置


因为最近项目需要重复部署,而且保证quartz定时任务不会重复执行,所以需要是quartz支持分布式,查阅资料后发现,quartz本身支持基于数据库的集群配置。大功告成之后写博客分享记录。

大致流程

1.把 QUARTZ 的 TASK 实例化进数据库, QUARTZ 只有实例化进入数据库后才能做集群,把quartz-1.8.4/docs/dbTables/tables_mysql_innodb.sql(因为我的mysql是innodb引擎) 在 mysql 中执行一下会生成 12 张表;但是需要注意的是这个sql文件需要修改,直接执行会报错,我在这里提供了修改正确后的sql。
2.生成 quartz.properties 文件,把它放在工程的 src 目录下,使其能够被编译时纳入 class path 。
3.自己实现反序列化类,在quartz.xml中指定此类和quartz.properties路径
4.配置quartz.xml
5.配置web.xml能够读取quartz.xml
6.测试

一般我们的开发人员都喜欢使用 SPRING+QUARTZ ,因此这个 quartz.properties 都不用怎么去写,但是在集群方案中 quartz.properties 必写,如果不写 quartz 会调用自身 jar 包中的 quartz.properties 作为默认属性文件,同时修改 quartz.xml 文件。

建表语句 tables_mysql_innodb.sql

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))
engine=InnoDB;

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),
INDEX (JOB_NAME, JOB_GROUP),
FOREIGN KEY (JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP))
engine=InnoDB;

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),
INDEX (JOB_NAME, JOB_GROUP),
FOREIGN KEY (JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP))
engine=InnoDB;

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),
INDEX (TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP))
engine=InnoDB;

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

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),
INDEX (TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP))
engine=InnoDB;

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),
INDEX (TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP))
engine=InnoDB;

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

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

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))
engine=InnoDB;

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))
engine=InnoDB;

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

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; 

定义一个任务

public class OrderTaskPrimary {
    protected void execute() {
                System.out.println("gogogogo!!!!!!");
    }

}

web.xml

添加如下配置,使其启动时读取quartz.xml

 <servlet>  
        <servlet-name>annomvcZrongPipe</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>   
        <init-param>    
            <param-name>contextConfigLocation</param-name>    
            <param-value>classpath:application-servlet.xml
                        classpath:quartz.xml
            </param-value>   
        </init-param>   
    <load-on-startup>1</load-on-startup> 
    </servlet> 

quartz.property

注意:打开集群模式
org.quartz.jobStore.isClustered = true

#============================================================================
# Configure Main Scheduler Properties  
#============================================================================
org.quartz.scheduler.instanceName = EventScheduler   
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 


#============================================================================
# Configure JobStore  
#============================================================================
org.quartz.jobStore.misfireThreshold = 60000 
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# mysql 
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# Oracle 
#org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.tablePrefix = QRTZ_ 
org.quartz.jobStore.maxMisfiresToHandleAtATime=10 
org.quartz.jobStore.isClustered = true  
org.quartz.jobStore.clusterCheckinInterval = 20000 

这个 MethodInvokingJobDetailFactoryBean 类中的 methodInvoking 方法,是不支持序列化的,因此在把 QUARTZ 的 TASK 序列化进入数据库时就会抛错。网上有说把 SPRING 源码拿来,修改一下这个方案,然后再打包成 SPRING.jar 发布,这些都是不好的方法,是不安全的。

必须根据 QuartzJobBean 来重写一个自己的类,然后使用 SPRING 把这个重写的类(我们就名命它为: MyDetailQuartzJobBean )注入 appContext 中后,再使用 AOP 技术反射出原有的 quartzJobx( 就是开发人员原来已经做好的用于执行 QUARTZ 的 JOB 的执行类 ) 。

MyDetailQuartzJobBean

import java.lang.reflect.Method;  

import org.apache.commons.logging.Log;  
import org.apache.commons.logging.LogFactory;  
import org.quartz.JobExecutionContext;  
import org.quartz.JobExecutionException;  
import org.springframework.context.ApplicationContext;  
import org.springframework.scheduling.quartz.QuartzJobBean;  

public class MyDetailQuartzJobBean  extends QuartzJobBean {  
    protected final Log logger = LogFactory.getLog(getClass());  
    private String targetObject;  
    private String targetMethod;  
    private ApplicationContext ctx;  

    @Override  
    protected void executeInternal(JobExecutionContext context)  
            throws JobExecutionException {  
        try {  
            logger.info("execute [" + targetObject + "] at once>>>>>>");  
            Object otargetObject = ctx.getBean(targetObject);  
            Method m = null;  

            try {  
                m = otargetObject.getClass().getMethod(targetMethod, new Class[] {JobExecutionContext.class});  
                m.invoke(otargetObject, new Object[] {context});  
            } catch (SecurityException e) {  
                logger.error(e);  
            } catch (NoSuchMethodException e) {  
                logger.error(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;  
    }  
}

quartz.xml

此处配置比较特别,需要指定序列化的类,即上面的类,因此配置文件的写法较单节点的略有区别

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    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">

    <!-- =====================主扫描下单任务========================== -->
    <!-- 定义目标bean和bean中的方法 -->  
    <bean id="Test" class="com.zr.pipe.quartz.OrderTaskPrimary" >
    </bean>  


    <bean id="OrderTaskPrimaryQtzJobMethod" class="org.springframework.scheduling.quartz.JobDetailBean">

        <property name="jobClass">

            <value>com.zr.pipe.quartz.MyDetailQuartzJobBean</value>

        </property>

        <property name="jobDataAsMap">
            <map>
                <entry key="targetObject" value="Test" />
                <entry key="targetMethod" value="execute" />
            </map>
        </property> 

    </bean>


    <bean id="OrderTaskPrimaryCronTriggerBean" class="org.springframework.scheduling.quartz.CronTriggerBean">
                                <property name="jobDetail">
                                                <ref bean="OrderTaskPrimaryQtzJobMethod" />
                                </property>
                                <property name="cronExpression">
                                                <value>0/5 * * * * ?</value>
                                </property>

                </bean>
    <!-- ======================== 调度工厂 ======================== -->  
    <!--  总管理类如果将lazy-init='false'那么容器启动就会执行调度程序   -->  
    <bean id="SpringJobSchedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"  lazy-init="false" >  
        <property name="configLocation" value="classpath:quartz.properties" />
        <property name="dataSource">
            <ref bean="dataSource1" />
        </property>
        <property name="applicationContextSchedulerContextKey" value="applicationContext" />
        <property name="triggers">  
            <list>   
                <ref local="OrderTaskPrimaryCronTriggerBean"/>

            </list>  
        </property>  
    </bean>



</beans>

测试:

几个节点都带有 quartz 任务,此时只有一台 quartz 在运行,另几个节点上的 quartz 没有运行。

此时手动 shutdown 那台运行 QUARTZ (在程序里加 system.out.println(“execute”), 运行 quartz 的那个节点在后台会打印 execute )的节点,过了 几 秒左右,另一个节点的 quartz 自动监测到了集群中运行着的 quartz 的 instance 已经 shutdown ,因此 quartz 集群会自动把任一台可用的 APP 上启动起一个 quartz job 的任务。


猜你喜欢

转载自blog.csdn.net/quguang65265/article/details/67069006