Java下Spring实现Quartz集群分布式(一)

一、序言

1、你了解 Quartz 吗?

Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。

Quartz 可以与 J2EE 与 J2SE 应用程序相结合也可以单独使用。

Quartz 允许程序开发人员根据时间的间隔来调度作业。

Quartz 实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。

2、Quartz 核心概念

 我们需要明白 Quartz 的几个核心概念,这样理解起 Quartz 的原理就会变得简单了。

1、Job 表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:

void execute(JobExecutionContext context)  

2、JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。 

3、Trigger 代表一个调度参数的配置,什么时候去调。

4、Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。 

3、Quartz的运行环境

1、Quartz 可以运行嵌入在另一个独立式应用程序。

2、Quartz 可以在应用程序服务器(或 servlet 容器)内被实例化,并且参与 XA 事务。

3、Quartz 可以作为一个独立的程序运行(其自己的 Java 虚拟机内),可以通过 RMI 使用。

4、Quartz 可以被实例化,作为独立的项目集群(负载平衡和故障转移功能),用于作业的执行。

4、Quartz的集群功能

Quartz的集群功能通过故障转移和负载平衡功能为您的调度程序带来高可用性和可扩展性。

使ç¨JDBC-JobStoreé置群é

目前,群集仅适用于JDBC-Jobstore(JobStoreTX或JobStoreCMT),并且基本上通过使群集的每个节点共享相同的数据库来工作。

负载平衡自动发生,群集的每个节点都尽可能快地触发jobs。当Triggers的触发时间发生时,获取它的第一个节点(通过在其上放置一个锁定)是将触发它的节点。

每次射击只能有一个节点开火。我的意思是,如果工作有一个重复的Triggers,告诉它每10秒钟发射一次,那么在12:00:00,正好一个节点将运行这个工作,在12:00:10,一个节点将运行作业等。它不一定是每次相同的节点 - 它或多或少是随机的,哪个节点运行它。负载平衡机制对于繁忙的调度器(大量的Triggers)是近似随机的,但是对于非忙(例如,很少的Triggers)调度器而言,有利于同一个节点。

当其中一个节点在执行一个或多个作业期间失败时发生故障切换。当节点出现故障时,其他节点会检测到该状况并识别数据库中在故障节点内正在进行的作业。任何标记为恢复的作业(在JobDetail上都具有“请求恢复”属性)将被剩余的节点重新执行。没有标记为恢复的作业将在下一次相关的Triggers触发时简单地被释放以执行。

集群功能最适合扩展长时间运行和/或cpu密集型作业(通过多个节点分配工作负载)。如果需要扩展以支持数千个短期运行(例如1秒)作业,则可以考虑通过使用多个不同的调度程序(包括HA的多个群集调度程序)对作业集进行分区。调度程序使用集群范围的锁,这种模式会在添加更多节点(超过三个节点 - 取决于数据库的功能等)时降低性能。

通过将“org.quartz.jobStore.isClustered”属性设置为“true”来启用聚类。集群中的每个实例都应该使用相同的quartz.properties文件。这样做的例外是使用相同的属性文件,具有以下允许的异常:不同的线程池大小,以及“org.quartz.scheduler.instanceId”属性的不同值。集群中的每个节点必须具有唯一的instanceId,通过将“AUTO”作为此属性的值,可以轻松完成(不需要不同的属性文件)。有关更多信息,请参阅有关JDBC-JobStore的配置属性的信息。

不要在单独的机器上运行群集,除非它们的时钟使用某种形式的时间同步服务(守护进程)进行同步,而这些时间同步服务(守护进程)运行非常有限(时钟必须在彼此之间)。
不要针对任何其他实例运行(start()ed)的同一组数据库表启动(scheduler.start())非集群实例。您可能会收到严重的数据损坏,一定会遇到不正常的行为。

二、下载安装

1、首先需要下载jar包,本文以2.2.3版本为例,官方下载地址:http://www.quartz-scheduler.org/downloads/

注:如果是maven工程则可使用下面的配置:

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
  </dependency>
  <dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.2.1</version>
  </dependency>   

2、下载后的解压的目录

3、项目使用有可能会用到lib目录下的包,如果是maven项目,自己在pom.xml中添加依赖的jar包,下面是lib目录中所有的jar包:

三、集群配置

1、持久化需要使用到数据库,quartz提供了各种数据库的脚本。脚本文件放在quartz-2.2.3-distribution.tar.gz\quartz-2.2.3\docs\dbTables目录下,这里以Mysql为例(注:如果使用其他的数据库则使用不同的Quartz数据库脚本)。

下面为mysql文件的具体数据库脚本: 

#
# In your Quartz properties file, you'll need to set 
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
#
# By: Ron Cordell - roncordell
#  I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.

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_SIMPROP_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(
SCHED_NAME VARCHAR(120) NOT NULL,
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_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) 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 (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
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 (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
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 (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (          
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

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

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

CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_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_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;

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

CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);

CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);

CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);

commit; 
 

2、数据库脚本导入完成之后,我们接下来需要对quartz进行一些配置,本文用spring来管理scheduler调度实例,spring管理配置的xml文件如下: 

<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.xsd">

    <bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!--<property name="applicationContextSchedulerContextKey" value="applicationContext"/>-->
        <property name="jobFactory">
            <bean class="wade.core.interceptors.SpringBeanJobFactory"/>
        </property>
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
        <property name="configLocation" value="classpath:/conf/quartz.properties"/>
    </bean>

</beans>

dataSouce的xml文件代码为:(本文使用的是C3P0连接池)

<!-- 配置连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
   <property name="driverClass">
      <value>${jdbc.driverClassName}</value>
   </property>
   <property name="jdbcUrl">
      <value>${jdbc.url}</value>
   </property>
   <property name="user">
      <value>${jdbc.username}</value>
   </property>
   <property name="password">
      <value>${jdbc.password}</value>
   </property>
   <!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
   <property name="initialPoolSize">
      <value>${c3p0.initialPoolSize}</value>
   </property>
   <!--连接池中保留的最小连接数。-->
   <property name="minPoolSize">
      <value>${c3p0.minPoolSize}</value>
   </property>
   <!--连接池中保留的最大连接数。Default: 15 -->
   <property name="maxPoolSize">
      <value>${c3p0.maxPoolSize}</value>
   </property>
   <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
   <property name="maxIdleTime">
      <value>${c3p0.maxIdleTime}</value>
   </property>
   <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
   <property name="acquireIncrement">
      <value>${c3p0.acquireIncrement}</value>
   </property>
   <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
         属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
         如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
   <property name="maxStatements">
      <value>${c3p0.maxStatements}</value>
   </property>
   <!--每60秒检查所有连接池中的空闲连接。Default: 0 -->
   <property name="idleConnectionTestPeriod">
      <value>${c3p0.idleConnectionTestPeriod}</value>
   </property>
   <!--两次连接中间隔时间,单位毫秒,默认为1000 -->
   <property name="acquireRetryDelay">
      <value>${c3p0.acquireRetryDelay}</value>
   </property>
   <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
   <property name="acquireRetryAttempts">
      <value>${c3p0.acquireRetryAttempts}</value>
   </property>
   <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效
         保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
         获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->
   <property name="breakAfterAcquireFailure">
      <value>${c3p0.breakAfterAcquireFailure}</value>
   </property>
   <!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的
         时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
         等方法来提升连接测试的性能。Default: false -->
   <property name="testConnectionOnCheckout">
      <value>${c3p0.testConnectionOnCheckout}</value>
   </property>
   <!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能
                     通过多线程实现多个操作同时被执行。Default: 3-->
   <property name="numHelperThreads">
      <value>${c3p0.numHelperThreads}</value>
   </property>
</bean>

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 = 2000
org.quartz.jobStore.dataSource = myDS

#==============================================================
#Configure DataSource配置数据源
#==============================================================
#org.quartz.dataSource.myDS.driver = oracle.jdbc.OracleDriver
#org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@127.0.0.1/orcl
#org.quartz.dataSource.myDS.user = dcap
#org.quartz.dataSource.myDS.password = dcap#2018
#org.quartz.dataSource.myDS.maxConnections = 30

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

注:本文的spring.xml的配置详解将在我的其他的quartz文章里详细解释。

3、创建两个job测试定时任务。

(1)在自定义的controller类里注入SchedulerFactoryBean(调度程序工程),此SchedulerFactoryBean已经被上文的xml引入,自动生成在spring的IOC容器中。

/**
 * 注入-调度程序工厂bean
 */
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;(2)

(2) 在自定义的controller类里定义创建两个job的方法(红色的quartzService在我的quartz其他文章会有源码和解释贴出来,它quartzService也是通过注入方式)

@Resource
public QuartzService quartzService;
@RequestMapping(params = "addJobone")
@ResponseBody
public AjaxJson addJob1(HttpServletRequest request) {
    try {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        quartzService.addJob("job1","default",
                (Class<? extends Job>) Class.forName("wade.system.job.TestJob"),"0 0/1 * * * ? ",null,scheduler,false,false);
    }catch (Exception e){
        System.out.println(e);
    }
    return new AjaxJson();

}

@RequestMapping(params = "addJobtwo")
@ResponseBody
public AjaxJson addJob2(HttpServletRequest request){
    try {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        quartzService.addJob("job2","default",
                (Class<? extends Job>) Class.forName("wade.system.job.TestJob2"),"0 0/1 * * * ? ",null,scheduler,false,false);
    }catch (Exception e){
        System.out.println(e);
    }
    return new AjaxJson();
}

(3)两个job分别都要实现job接口,复写execute()方法,当调度程序调度job任务时,就会执行execute里的方法内容。

package wade.system.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class TestJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("job1运行");
    }
}
package wade.system.job;


import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class TestJob2 implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("job2运行");
    }
}

四、集群实验

(1)启动项目,出现以下信息则启动成功。

(2)调度器增加了两个job任务,即调用上文的自定义controller类的创建两个job的方法,作业名称分别为job1和job2.

(3)观察tomcatA的运行情况,我们可发现job1和job2都是每一分钟执行一次,这和我们上面配置的cron表达式一致。

(4)关键的集群测试步骤来了,我们打开另一个tomcatB运行相同的项目,观察现象。

tomcatA的控制输出台:

 

tomcatB的控制输出台: 

实验总结:我们能够发现两台集群的服务器都会共同执行job任务,当然同一个job在一个时间点只会执行一次,只是有时候由TomcatA执行,有时候由TomcatB执行,这样就实现了集群,分担了tomcat的压力。而且当TomcatA发生故障挂掉的时候,TomcatB能够正常执行job任务,不会使得调度系统也挂掉,不过此时TomcatB承担所有的job的调度任务。

 

猜你喜欢

转载自blog.csdn.net/zengwende/article/details/87647458