1. 场景介绍

好吧,笔者所做的是一个小项目,虽说项目不大,但是项目也是集群部署的,项目中任务调度采用的是quartz,jobstore存储在ram,当然这对于一些重复执行无重大影响的任务并没有什么问题,但是后台中有几个任务,例如数据统计任务,会导致quartz定时任务在多个节点重复执行,造成了资源消耗;
我们的需求: 集群任务:下发一个集群任务时,执行任务节点的重启和销毁并不会影响任务执行,假设任务在该节点执行中,该节点因异常原因被销毁,调度中心需要将该任务重新下发到正常节点中执行
非集群任务: 各个节点各自执行,不用关心其他节点执行情况
恰巧我们的项目中既有集群任务,也有非集群任务,所以在不增加系统复杂度的情况下使用quartz任务调度解决集群任务调度是首选,非集群任务调度,quartz本身也支持,不过,一些小任务,没必要使用,所以采用的是spring task作为轻型任务调度

2. 本例实现

实现手段:quartz集群任务+spring task 轻型任务调度
官网不知咋地,无法访问,所幸github地址还可用: https://github.com/quartz-scheduler/quartz

mysql quartz 表结构创建,一共11张表

# 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 ( 190 ) NOT NULL , JOB_GROUP VARCHAR ( 190 ) 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 ( 190 ) NOT NULL , TRIGGER_GROUP VARCHAR ( 190 ) NOT NULL , JOB_NAME VARCHAR ( 190 ) NOT NULL , JOB_GROUP VARCHAR ( 190 ) 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 ( 190 ) 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 ( 190 ) NOT NULL , TRIGGER_GROUP VARCHAR ( 190 ) 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 ( 190 ) NOT NULL , TRIGGER_GROUP VARCHAR ( 190 ) 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 ( 190 ) NOT NULL , TRIGGER_GROUP VARCHAR ( 190 ) 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 ( 190 ) NOT NULL , TRIGGER_GROUP VARCHAR ( 190 ) 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 ( 190 ) 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 ( 190 ) 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 ( 190 ) NOT NULL , TRIGGER_GROUP VARCHAR ( 190 ) NOT NULL , INSTANCE_NAME VARCHAR ( 190 ) 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 ( 190 ) NULL , JOB_GROUP VARCHAR ( 190 ) 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 ( 190 ) 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 ;

quartz.properties配置

jobstore 为ram的配置

org.quartz.scheduler.instanceName = test  
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.simpl.RAMJobStore

jobstore 为jdbc的配置

org.quartz.scheduler.instanceName = test  
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

配置详解:https://blog.csdn.net/sanfye/article/details/49204307

示例任务编写及spring配置

非集群任务:
@Component
public class HelloJob {
    public void sayHello(){
        System.out.println("hello 非集群");
集群任务:
@PersistJobDataAfterExecution
@DisallowConcurrentExecution// 不允许并发执行
public class NewJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("test execute now   集群:"+System.currentTimeMillis());

spring 配置

<?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:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
            http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd">
定时任务分类: 
 集群模式 : 所有节点,只有一个节点最终执行
 非集群模式:各个节点都执行
    <context:component-scan base-package="com.ncs.quartz" />
    <!-- 配置数据库连接池 -->
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!-- 配置数据源 C3P0 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="minPoolSize" value="${jdbc.minPoolSize}" />
        <property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}" />
        <property name="acquireIncrement" value="${jdbc.acquireIncrement}" /> <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。 -->
        <property name="maxIdleTime" value="${jdbc.maxIdleTime}" />
        <property name="testConnectionOnCheckin" value="false" /><!--如果设为true那么在取得连接的同时将校验连接的有效性。Default: 
            false -->
        <property name="testConnectionOnCheckout" value="true" />
    </bean>
    <bean name="quartzScheduler"
        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="dataSource" ref="dataSource" /> 
        <!-- 可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->
        <property name="overwriteExistingJobs" value="true" />
        <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
        <property name="configLocation" value="classpath:quartz.properties" />
        <!-- 必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动 -->
        <property name="startupDelay" value="2" />
        <!-- 重要:autowireJobFactory 是为了解决Spring quartz Job不能依赖注入。 -->
        <property name="jobFactory" ref="autowireJobFactory" />
        <property name="triggers">
                <ref bean="news_trigger" />
            </list>
        </property>
    </bean>
    <bean id="news_jobDetail"
        class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value="com.ncs.quartz.job.NewJob" />
        <property name="durability" value="true" />
        <property name="requestsRecovery" value="true" />
        <property name="name" value="news_jobDetail"/>
    </bean>
    <bean id="news_trigger"
        class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="news_jobDetail" />
        <property name="cronExpression" value="0/10 * * * * ?" />
        <property name="timeZone" value="GMT+8:00" />
        <property name="name" value="news_trigger"/>
    </bean>
    <!-- 内存调度器 每个节点自行调度 -->
    <task:scheduler id="springRAMScheduler" pool-size="1"  />
    <task:scheduled-tasks scheduler="springRAMScheduler"  >
     <task:scheduled ref="helloJob" method="sayHello" cron="0/1 * * * * ?"  />
    </task:scheduled-tasks>
</beans>

注意点:由于任务的实例化是由quartz来实现,没有注入spring容器,所以在job中依赖其他容器中bean十分不方便,所以需要实现jobfactory,把实例化的bean注入spring容器即可
bean工厂如下:

* @author 桃源 * 2018年8月29日 上午11:18:32 * userfor:将quartzjob 注入spring容器,使得jobbean可以使用 Autowire 依赖容器对象 @Component public class AutowireJobFactory extends SpringBeanJobFactory { @Autowired private AutowireCapableBeanFactory capableBeanFactory; @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { // 调用父类的方法 Object jobInstance = super.createJobInstance(bundle); // 进行注入 capableBeanFactory.autowireBean(jobInstance); return jobInstance;

从图中可以看到,hello job 是在每个节点中都执行的
test job 只在其中一个节点执行
每个节点通过db锁来抢占任务的执行权
我们关掉节点1,会发现节点2会执行集群任务,只要节点中有可运行的节点,任务最终就会执行成功

3. 扩展

本案例适合一些小项目,不增加系统复杂度,方便维护
如果系统中调度程序多,节点也多,java中开源调度任务框架也有很多
例如:
Elastic-Job:http://elasticjob.io/index_zh.html
xxl-job:https://github.com/xuxueli/xxl-job
各位可以自行了解下

1. 场景介绍好吧,笔者所做的是一个小项目,虽说项目不大,但是项目也是集群部署的,项目中任务调度采用的是quartz,jobstore存储在ram,当然这对于一些重复执行无重大影响的任务并没有什么问题,但是后台中有几个任务,例如数据统计任务,会导致quartz定时任务在多个节点重复执行,造成了资源消耗; 我们的需求: 集群任务:下发一个集群任务时,执行任务节点的重启和销毁并不会影响任务执行...
一、前言:       虽然单个Quartz实例能给予我们很好的任务job调度能力,但它不能满足典型的企业需求,如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job,Quartz集群势必成为你应用的一部分了。使用 Quartz集群能力可以更好的支持你的业务需求,并且即使是其中一台机器在最糟的时间挂掉了也能确保所有的 Job 得到执行。 二、Quart
假如你让一个集群Quartz 应用与集群节点并行着运行,设法使用 JobInitializationPlugin和 RAMJobStore Quartz支持可选节点执行jobquartz集群,会自动将触发的job均衡的分发到各个节点。不过我现在有一个特殊的job,希望触发后可以在每个节点(或是指定的节点)执行。 百度、Google 了半天。。。没找到答案。 后来自己...
在每月的某一天 在一年中的某些日子 特定时间除外的时间(如商业节假日除外)not on certain days listed within a registered Calendar (such as business holidays) 重复特定次数
一、问题背景 美团CRM系统中每天有大量的后台任务需要调度执行,如构建索引、统计报表、周期同步数据等等,要求任务调度系统具备高可用性、负载均衡特性,可以管理并监控任务的执行流程,以保证任务的正确执行。 二、历史方案 美团CRM系统的任务调度模块经历了以下历史方案。 1. Crontab+SQL 每天晚上运行定时任务,通过SQL脚本+crontab方式执行,例如, 0 2 * * * /xxx/mtcrm/shell/mtcrm_daily_stat.sql //每天凌晨2:0..
有关quartz集群的配置方案,大家可以参阅博文《Spring之——quartz集群(Oracle数据源)》和《Spring之——quartz集群(MySQL数据源)》 quartz存储job方式就分三种,我们最常用的也是quartz默认的是RAMJobStore,RAMJobStore顾名思义就是把job的相关信息存储在内存里,如果用spring配置quartz的job信息的话,所有信息是
在测试时由于先测试的集群,导致在测试集群工作线程调度任务时,集群同样会调起任务,不断调试,查看源码,均未发现异常,最后查看文档,原来quartz有3种JOB存储方式,集群存储在内存中,而集群是持久化在了数据库中,启动服务时,首先后加载配置文件中的job信息并持久化到数据库中,由于没有清理表记录,所以集群在启动时,虽然从配置文件中没有加载到任务信息,但从数据库中获取到了,所以导致了这一现象。
集群环境下,大家会碰到一直困扰的问题,即多个 APP 下如何用 quartz 协调处理自动化 JOB 。 大家想象一下,现在有 A , B , C3 台机器同时作为集群服务器对外统一提供 SERVICE : A , B , C 3 台机器上各有一个 QUARTZ ,他们会按照即定的 SCHEDULE 自动执行各自的任务。 我们先不说实现什么功能,就说这样的架构其实有点像多线程。
1、Quartz任务调度的基本实现原理   Quartz是OpenSymphony开源组织在任务调度领域的一个开源项目,完全基于Java实现。作为一个优秀的开源调度框架,Quartz具有以下特点:     (1)强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;     (2)灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;     (3...