查看原文
其他

Spring整合Quartz定时任务,在集群、分布式系统中的应用

点击上方 "程序员小乐"关注, 星标或置顶一起成长

每天凌晨00点00分, 第一时间与你相约


每日英文

Everyone's got a life that no one else knows about.

每个人都有自己的人生,冷暖自知。


每日掏心

若要自由,就得牺牲安全。若要闲散,就不能获得别人评价中的成就。若要愉悦,就无须计较身边的人给予的态度。




来自:sundoctor | 责编:乐乐

链接:iteye.com/blog/sundoctor-486055

程序员小乐(ID:study_tech)第 787 次推文   图片来自Pexels


往日回顾:流行的Spring Boot + Vue架构整合开发的网易云+QQ音乐(附源码)!



     

   正文   


一、概述

虽然单个Quartz实例能给予你很好的Job调度能力,但它不能满足典型的企业需求,如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job,Quartz集群势必成为你应用的一部分了。

使用 Quartz 的集群能力可以更好的支持你的业务需求,并且即使是其中一台机器在最糟的时间崩溃了也能确保所有的 Job 得到执行。

二、Quartz 中集群如何工作

一个 Quartz 集群中的每个节点是一个独立的 Quartz 应用,它又管理着其他的节点。意思是你必须对每个节点分别启动或停止。不像许多应用服务器的集群,独立的 Quartz 节点并不与另一其的节点或是管理节点通信。Quartz 应用是通过数据库表来感知到另一应用的。

图:表示了每个节点直接与数据库通信,若离开数据库将对其他节点一无所知

三、创建Quartz数据库表

因为Quartz 集群依赖于数据库,所以必须首先创建Quartz数据库表。Quartz 包括了所有被支持的数据库平台的 SQL 脚本。在 <quartz_home>/docs/dbTables 目录下找到那些 SQL 脚本,这里的 <quartz_home> 是解压 Quartz 分发包后的目录。

这里采用的Quartz 2.2.3版本,总共11张表,不同版本,表个数可能不同。数据库为mysql,用tables_mysql_innodb.sql创建数据库表。

#
# 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(120NOT NULL,
JOB_NAME VARCHAR(200NOT NULL,
JOB_GROUP VARCHAR(200NOT NULL,
DESCRIPTION VARCHAR(250NULL,
JOB_CLASS_NAME VARCHAR(250NOT NULL,
IS_DURABLE VARCHAR(1NOT NULL,
IS_NONCONCURRENT VARCHAR(1NOT NULL,
IS_UPDATE_DATA VARCHAR(1NOT NULL,
REQUESTS_RECOVERY VARCHAR(1NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120NOT NULL,
TRIGGER_NAME VARCHAR(200NOT NULL,
TRIGGER_GROUP VARCHAR(200NOT NULL,
JOB_NAME VARCHAR(200NOT NULL,
JOB_GROUP VARCHAR(200NOT NULL,
DESCRIPTION VARCHAR(250NULL,
NEXT_FIRE_TIME BIGINT(13NULL,
PREV_FIRE_TIME BIGINT(13NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16NOT NULL,
TRIGGER_TYPE VARCHAR(8NOT NULL,
START_TIME BIGINT(13NOT NULL,
END_TIME BIGINT(13NULL,
CALENDAR_NAME VARCHAR(200NULL,
MISFIRE_INSTR SMALLINT(2NULL,
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(120NOT NULL,
TRIGGER_NAME VARCHAR(200NOT NULL,
TRIGGER_GROUP VARCHAR(200NOT NULL,
REPEAT_COUNT BIGINT(7NOT NULL,
REPEAT_INTERVAL BIGINT(12NOT NULL,
TIMES_TRIGGERED BIGINT(10NOT 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(120NOT NULL,
TRIGGER_NAME VARCHAR(200NOT NULL,
TRIGGER_GROUP VARCHAR(200NOT NULL,
CRON_EXPRESSION VARCHAR(120NOT 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(120NOT NULL,
    TRIGGER_NAME VARCHAR(200NOT NULL,
    TRIGGER_GROUP VARCHAR(200NOT NULL,
    STR_PROP_1 VARCHAR(512NULL,
    STR_PROP_2 VARCHAR(512NULL,
    STR_PROP_3 VARCHAR(512NULL,
    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,4NULL,
    DEC_PROP_2 NUMERIC(13,4NULL,
    BOOL_PROP_1 VARCHAR(1NULL,
    BOOL_PROP_2 VARCHAR(1NULL,
    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(120NOT NULL,
TRIGGER_NAME VARCHAR(200NOT NULL,
TRIGGER_GROUP VARCHAR(200NOT 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(120NOT NULL,
CALENDAR_NAME VARCHAR(200NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;

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

CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120NOT NULL,
ENTRY_ID VARCHAR(95NOT NULL,
TRIGGER_NAME VARCHAR(200NOT NULL,
TRIGGER_GROUP VARCHAR(200NOT NULL,
INSTANCE_NAME VARCHAR(200NOT NULL,
FIRED_TIME BIGINT(13NOT NULL,
SCHED_TIME BIGINT(13NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16NOT NULL,
JOB_NAME VARCHAR(200NULL,
JOB_GROUP VARCHAR(200NULL,
IS_NONCONCURRENT VARCHAR(1NULL,
REQUESTS_RECOVERY VARCHAR(1NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;

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

CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120NOT NULL,
LOCK_NAME VARCHAR(40NOT 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;


四、配置数据库连接池


1.配置jdbc.properties文件


jdbc.url=jdbc\:mysql\://192.168.1.132\:3306/jiafuwei?useUnicode\=true&characterEncoding\=utf8&autoReconnect\=true
jdbc.username=root
jdbc.password=123456
jdbc.driverClassName=com.mysql.jdbc.Driver


2.配置applicationContext.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" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="   
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
   ">


    <context:component-scan base-package="com.sundoctor" />

    <!-- 属性文件读入 -->
    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
            </list>
        </property>
    </bean>


    <!-- 数据源定义,使用c3p0 连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">

        <property name="driverClass" value="${jdbc.driverClassName}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="initialPoolSize" value="2" />
        <property name="minPoolSize" value="10" />
        <property name="maxPoolSize" value="20" />
        <property name="acquireIncrement" value="2" />
        <property name="maxIdleTime" value="1800" />
    </bean>
</beans>


五、创建Job测试服务类


package com.sundoctor.quartz.cluster.example;

import java.io.Serializable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;


@Service("simpleService")
public class SimpleService {

    private static final long serialVersionUID = 122323233244334343L;
    private static final Logger logger = LoggerFactory.getLogger(SimpleService.class);

    public void testMethod1(){
        //这里执行定时调度业务
        logger.info("testMethod1.......1");
        System.out.println("2--testMethod1......."+System.currentTimeMillis()/1000);
    }

    public void testMethod2(){
        logger.info("testMethod2.......2");    
    }
}


六、创建一个Job类MyQuartzJobBean1


package com.sundoctor.quartz.cluster.example;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;

@PersistJobDataAfterExecution
@DisallowConcurrentExecution// 不允许并发执行
public class MyQuartzJobBean1 extends QuartzJobBean {

    private static final Logger logger = LoggerFactory.getLogger(MyQuartzJobBean1.class);

    @Override
    protected void executeInternal(JobExecutionContext jobexecutioncontext) throws JobExecutionException {

        SimpleService simpleService = getApplicationContext(jobexecutioncontext).getBean("simpleService",
                SimpleService.class);
        simpleService.testMethod1();

    }

    private ApplicationContext getApplicationContext(final JobExecutionContext jobexecutioncontext) {
        try {
            return (ApplicationContext) jobexecutioncontext.getScheduler().getContext().get("applicationContextKey");
        } catch (SchedulerException e) {
            logger.error("jobexecutioncontext.getScheduler().getContext() error!", e);
            throw new RuntimeException(e);
        }
    }

}


七、配置 Quartz 使用集群


1.配置节点的 quartz.properties 文件


org.quartz.scheduler.instanceName = TestScheduler1  
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 = 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 
org.quartz.jobStore.clusterCheckinInterval = 20000

org.quartz.scheduler.instanceName属性可为任何值,用在 JDBC JobStore 中来唯一标识实例,但是所有集群节点中必须相同。

org.quartz.scheduler.instanceId属性为 AUTO即可,基于主机名和时间戳来产生实例 ID。

org.quartz.jobStore.class属性为 JobStoreTX,将任务持久化到数据中。因为集群中节点依赖于数据库来传播 Scheduler 实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。这意味着你必须使用 JobStoreTX 或是 JobStoreCMT 作为 Job 存储;你不能在集群中使用 RAMJobStore。

org.quartz.jobStore.isClustered 属性为 true,你就告诉了 Scheduler 实例要它参与到一个集群当中。这一属性会贯穿于调度框架的始终,用于修改集群环境中操作的默认行为。

org.quartz.jobStore.clusterCheckinInterval 属性定义了Scheduler 实例检入到数据库中的频率(单位:毫秒)。Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。通过检入操作,Scheduler 也会更新自身的状态记录。

clusterChedkinInterval 越小,Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)。

扩展:SpringBoot内容聚合

2.配置applicationContext-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.xsd">


    <bean name="quartzScheduler"
        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
        <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
        <property name="configLocation" value="classpath:quartz.properties" />        
        <property name="triggers">
            <list>
                <ref bean="trigger1" />
            </list>
        </property>
    </bean>

    <bean id="jobDetail1" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass">
            <value>com.sundoctor.quartz.cluster.example.MyQuartzJobBean1</value>
        </property>    
        <property name="durability" value="true" />    
        <property name="requestsRecovery" value="true" />        
    </bean>
    <bean id="trigger1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="jobDetail1" />
        <property name="cronExpression" value="0/10 * * * * ?" />
    </bean>



</beans>

dataSource:项目中用到的数据源,里面包含了quartz用到的11张数据库表;

applicationContextSchedulerContextKey:是org.springframework.scheduling.quartz.SchedulerFactoryBean这个类中把spring上下 文以key/value的方式存放在了SchedulerContext中了,可以用applicationContextSchedulerContextKey所 定义的key得到对应spring 的ApplicationContext;

configLocation:用于指明quartz的配置文件的位置

requestsRecovery:requestsRecovery属性必须设置为 true,当Quartz服务被中止后,再次启动或集群中其他机器接手任务时会尝试恢复执行之前未完成的所有任务。

八、运行Quartz集群

在相同或不同的机器上运行com.sundoctor.quartz.cluster.example.test.MainTest进行测试,在本例中只是简单打印一下日志。

package com.sundoctor.quartz.cluster.example.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest {

    /**
     * @param args
     */

    public static void main(String[] args) {
        ApplicationContext springContext = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext.xml","classpath:applicationContext-quartz.xml"});
    }

}

Quartz 实际并不关心你是在相同的还是不同的机器上运行节点。当集群是放置在不同的机器上时,通常称之为水平集群。节点是跑在同一台机器是,称之为垂直集群。对于垂直集群,存在着单点故障的问题。这对高可用性的应用来说是个坏消息,因为一旦机器崩溃了,所有的节点也就被有效的终止了。

当你运行水平集群时,时钟应当要同步,以免出现离奇且不可预知的行为。假如时钟没能够同步,Scheduler 实例将对其他节点的状态产生混乱。有几种简单的方法来保证时钟何持同步,而且也没有理由不这么做。最简单的同步计算机时钟的方式是使用某一个 Internet 时间服务器(Internet Time Server ITS)。

没什么会阻止你在相同环境中使用集群的和非集群的 Quartz 应用。唯一要注意的是这两个环境不要混用在相同的数据库表。意思是非集群环境不要使用与集群应用相同的一套数据库表;否则将得到希奇古怪的结果,集群和非集群的 Job 都会遇到问题。

假如你让一个非集群的 Quartz 应用与集群节点并行着运行,设法使用 JobInitializationPlugin和 RAMJobStore。

项目下载地址(里面包含所需的jar):

files.cnblogs.com/files/jiafuwei/SpringTest.zip

在eclipse的两个工作空间同时开启这两个项目,连接同一个mysql数据库,发现只有一个定时任务在运行,停掉其中一个项目,另外一个项目的定时任务开启了。

欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。

欢迎各位读者加入订阅号程序员小乐技术群,在后台回复“加群”或者“学习”即可。

猜你还想看


阿里、腾讯、百度、华为、京东最新面试题汇集

Java 多态的实现机制,看了都说好!

中央政治局会议:疫情防控工作取得阶段性成效,但拐点尚未到来

在Spring Boot项目中整合使用Activiti

关注订阅号「程序员小乐」,收看更多精彩内容
嘿,你在看吗?
: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存