• Quartz2.0以上版本的单机和集群


    (一)Quartz单机

    1.Quartz简介

      Quartz是一个完全由java编写的开源作业调度框架,能实现业务的定时调度。Quartz主要有三个核心调度器、任务和触发器:

    ①任务-JobDetail:Quartz在任务调度的时候,需要创建一个任务实例,JobDetail正是承担这个角色。在Quartz2.0以前的版本中,创建一个任务通过new JobDetail(String jobName, String gruop, Class jobCLass)这个方法来创建,在2.0之后的版本中通过JobBuilder来创建任务。Job接口中只有一个方法void execute(JobExecutionContext context) throws JobExecutionException,因此在任务调度时,只需要实现execute方法就可以了,使用起来很方便。

    ②触发器-Trigger:定义Job的执行时间、次数等信息,有SimpleTrigger和CronTrigger两种类型。当你需要的是一次性的调度,或者你需要在指定的时间激活某个任务并执行N次,设置每次任务执行的间隔时间T。那此时使用SimpleTrigger将是非常方便的。如果你想在每周的周一7:00或者每月的第2,3天实现任务调度,这时候就需要用到CronTrigger,CronTrigger能够提供复杂的触发器表达式满足我们的需求。同时需要提到的一点是Quartz还提供很多日期的实现类DateBuilder等,在实际使用中很方便。

    ③调度器-Scheduler:Quartz框架的核心是调度器。调度器负责管理Quartz应用运行时环境。调度器不是靠自己做所有的工作,而是依赖框架内一些非常重要的部件。Quartz不仅仅是线程和线程管理。为确保可伸缩性,Quartz采用了基于多线程的架构。启动时,框架初始化一套worker线程,这套线程被调度器用来执行预定的作业。这就是Quartz怎样能并发运行多个作业的原理。Quartz依赖一套松耦合的线程池管理部件来管理线程环境。

    2.搭建Quartz工程

    ①创建一个新工程

      新建文件lib作为外部jar包,Quartz最新版本通过官网可以下载http://www.quartz-scheduler.org/downloads/导入你下载的quartz包,新建package为com.example.singleQuartz,里面就两个简单的类SingleQuartzJob和SingleQuartzServer。SingleQuartzJob定义Job的实现类,SingleQuartzServer任务调度服务。

    ②SingleQuartzJob.java定义Job的实现类

    package com.example.singleQuartz;
    
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    
    import java.util.Date;
    
    /**
     * Created by XuHui on 2016/12/22.
     */
    public class SingleQuartzJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.print("Hello, Quartz! - executing its JOB at "+
                    new Date() + " by " + context.getTrigger().getJobKey() + "
    ");
        }
    }

    ③SingleQuartzServer.java实现任务调度

    package com.example.singleQuartz;
    
    import org.quartz.*;
    import org.quartz.impl.StdSchedulerFactory;
    
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by XuHui on 2016/12/22.
     */
    public class SingleQuartzServer {
        public static void main(String[] args) throws SchedulerException {
            /* 获取Scheduler实例 */
            SchedulerFactory factory = new StdSchedulerFactory();
            Scheduler scheduler = factory.getScheduler();
    
            /* 启动调度器 */
            scheduler.start();
    
            /* 任务:withIdentity(String name, String group),其中groupName可以自己定义,也可以用Scheduler提供的DEFAULT_GROUP。
                    这里要求同组里面的jobName不能相同*/
            JobDetail jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
                    .withIdentity("job", Scheduler.DEFAULT_GROUP)
                    .build();
    
            /*触发器:SimpleTrigger和CronTrigger。实现的功能是:接下来30s后执行job,以后每个10s执行一次,重复10次,一共执行11次。
                      nextGivenSecondDate(null, 30)距离现在时间30s之后执行job,此处null可写作new Date(),可自行在api查看源码实现 */
            Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
            SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
                    .withIdentity("trigger", Scheduler.DEFAULT_GROUP)
                    .startAt(startTime)
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInSeconds(10)
                            .withRepeatCount(10))
                    .forJob(jobDetail)
                    .build();
    
            /* 交由调度器调度Job */
            scheduler.scheduleJob(jobDetail, simpleTrigger);
    
            /* 3分钟任务调度之后关闭调度器 */
            try{
                TimeUnit.MINUTES.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                scheduler.shutdown();
            }
        }
    }
    View Code

    ④执行任务调度

      执行run->main但是发现报错了。此时是少了两个个日志包,slf4j-api-1.7.22.jar和slf4j-simple-1.7.22.jar,我们只要添加slf4j包就可以了。

    通过官网可以下载http://www.slf4j.org/download.html,找到这两个包放到lib下就可以了。

    Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
        at org.quartz.impl.StdSchedulerFactory.<init>(StdSchedulerFactory.java:303)
        at com.example.singleQuartz.SingleQuartzServer.main(SingleQuartzServer.java:15)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
    Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 7 more

    解决完上面日志包的问题,我们运行上面程序可以看到正常的调度结果如下。

    
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:40:30 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:40:40 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:40:50 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:00 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:10 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:20 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:30 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:40 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:50 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:42:00 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:42:10 CST 2016 by DEFAULT.job
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down.
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused.
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.

     ⑤任务调度的多线程

      Scheduler是存在多对多的关系,由于线程池的存在,调度器实现多线程并发执行任务调度,直接看下面demo就能明白。

    package com.example.singleQuartz;
    
    import org.quartz.*;
    import org.quartz.impl.StdSchedulerFactory;
    
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by XuHui on 2016/12/22.
     */
    public class SingleQuartzThreadPoolServer {
        public static void main(String[] args) throws SchedulerException {
            /* 获取Scheduler实例 */
            SchedulerFactory factory = new StdSchedulerFactory();
            Scheduler scheduler = factory.getScheduler();
    
            /* 启动调度器 */
            scheduler.start();
    
            /******************************************************* Job-1 ****************************************************************/
            /* 任务:withIdentity(String name, String group),其中groupName可以自己定义,也可以用Scheduler提供的DEFAULT_GROUP。
                    这里要求同组里面的jobName不能相同*/
            JobDetail jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
                    .withIdentity("job1", Scheduler.DEFAULT_GROUP)
                    .build();
    
            /*触发器:SimpleTrigger和CronTrigger。实现的功能是:接下来30s后执行job,以后每个10s执行一次,重复10次,一共执行11次。
                      nextGivenSecondDate(null, 30)距离现在时间30s之后执行job,此处null可写作new Date(),可自行在api查看源码实现 */
            Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
            SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
                    .withIdentity("trigger1", Scheduler.DEFAULT_GROUP)
                    .startAt(startTime)
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInSeconds(10)
                            .withRepeatCount(2))
                    .forJob(jobDetail)
                    .build();
    
            /* 交由调度器调度Job */
            scheduler.scheduleJob(jobDetail, simpleTrigger);
    
            /******************************************************* Job-2 ****************************************************************/
            jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
                    .withIdentity("job2", Scheduler.DEFAULT_GROUP)
                    .build();
    
            simpleTrigger = TriggerBuilder.newTrigger()
                    .withIdentity("trigger2", Scheduler.DEFAULT_GROUP)
                    .startAt(startTime)
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInSeconds(10)
                            .withRepeatCount(2))
                    .forJob(jobDetail)
                    .build();
            scheduler.scheduleJob(jobDetail, simpleTrigger);
    
            /******************************************************* Job-3 ****************************************************************/
            jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
                    .withIdentity("job3", Scheduler.DEFAULT_GROUP)
                    .build();
    
            simpleTrigger = TriggerBuilder.newTrigger()
                    .withIdentity("trigger3", Scheduler.DEFAULT_GROUP)
                    .startAt(startTime)
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInSeconds(10)
                            .withRepeatCount(2))
                    .forJob(jobDetail)
                    .build();
            scheduler.scheduleJob(jobDetail, simpleTrigger);
    
            /* 2分钟任务调度之后关闭调度器 */
            try{
                TimeUnit.MINUTES.sleep(2);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                scheduler.shutdown();
            }
        }
    
    }
    View Code

    执行结果如下:

    [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:30 CST 2016 by DEFAULT.job1
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:30 CST 2016 by DEFAULT.job2
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:30 CST 2016 by DEFAULT.job3
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:40 CST 2016 by DEFAULT.job3
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:40 CST 2016 by DEFAULT.job1
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:40 CST 2016 by DEFAULT.job2
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:50 CST 2016 by DEFAULT.job1
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:50 CST 2016 by DEFAULT.job3
    Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:50 CST 2016 by DEFAULT.job2
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down.
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused.
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.

     (二)任务调度持久到数据库

    1.建立数据库和建表

      上面的例子的任务是存在内存中的,如果程序关闭任务就会消失,为了保持任务的可持久性,需要将任务调度存到数据库中。在你下载的quartz包下找到quartz-2.2.1docsdbTables,找到你对应的数据库建表语言,这里我用的是mysql数据库tables_mysql.sql。在你本地或者服务器数据库新建一个数据库quartz(你可以不建,主要为了和其他数据库区分)。将以下的建表语言导入该数据库。

    #
    # Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
    #
    # PLEASE consider using mysql with innodb tables to avoid locking issues
    #
    # In your Quartz properties file, you'll need to set 
    # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
    #
    
    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)
    );
    
    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)
    );
    
    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)
    );
    
    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(200) 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)
    );
    
    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)
    );
    
    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),
        FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
            REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    );
    
    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)
    );
    
    CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
      (
        SCHED_NAME VARCHAR(120) NOT NULL,
        TRIGGER_GROUP  VARCHAR(200) NOT NULL, 
        PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
    );
    
    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)
    );
    
    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)
    );
    
    CREATE TABLE QRTZ_LOCKS
      (
        SCHED_NAME VARCHAR(120) NOT NULL,
        LOCK_NAME  VARCHAR(40) NOT NULL, 
        PRIMARY KEY (SCHED_NAME,LOCK_NAME)
    );
    
    
    commit;
    View Code

    2.导入mysql数据库的jdbc驱动包,mysql-connector-java-5.1.40-bin.jar

    3.配置Quartz配置文件quartz.properities

    org.quartz.scheduler.instanceName = MyScheduler
    org.quartz.threadPool.threadCount = 3
    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.dataSource = myDS
    
    org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
    org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8
    org.quartz.dataSource.myDS.user = root
    org.quartz.dataSource.myDS.password = 123456
    org.quartz.dataSource.myDS.maxConnections = 5

    4.任务调度的持久性

      其实介绍这部分主要是为了后面的Quartz集群做铺垫。为了验证任务调度的可持久性,我们从这两方面验证,当我们程序停止运行的时候,查看数据库能查看到任务调度的数据;当重新启动同一调度器的时候,原来的任务能继续执行。SingleQuartzKeepAlive.java如下,其实和上面的SingleQuartzServer基本一样,只是加载了Quartz配置文件。

    package com.example.singleQuartz;
    
    import org.quartz.*;
    import org.quartz.impl.StdSchedulerFactory;
    
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by XuHui on 2016/12/22.
     */
    public class SingleQuartzKeepAlive {
        public static void main(String[] args) throws SchedulerException {
             /* 获取Scheduler实例 */
            StdSchedulerFactory factory = new StdSchedulerFactory();
            factory.initialize("quartz.properities");
            Scheduler scheduler = factory.getScheduler();
    
            /* 启动调度器 */
            scheduler.start();
    
            JobDetail jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
                    .withIdentity("job", Scheduler.DEFAULT_GROUP)
                    .build();
    
            Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
            SimpleTrigger simpleTrigger = (SimpleTrigger) TriggerBuilder.newTrigger()
                    .withIdentity("trigger", Scheduler.DEFAULT_GROUP)
                    .startAt(startTime)
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInSeconds(10)
                            .withRepeatCount(10))
                    .forJob(jobDetail)
                    .build();
    
            /* 交由调度器调度Job */
            scheduler.scheduleJob(jobDetail, simpleTrigger);
    
            /* 3分钟任务调度之后关闭调度器 */
            try{
                TimeUnit.MINUTES.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                scheduler.shutdown();
            }
        }
    }
    View Code

    我们run->main发现报错了,报错如下。这个因为我们缺少了c3p0-0.9.1.2.jar,下载地址为http://www.mchange.com/projects/c3p0/index.html。将这个包加入lib中即可。

    Exception in thread "main" java.lang.NoClassDefFoundError: com/mchange/v2/c3p0/ComboPooledDataSource
        at org.quartz.utils.PoolingConnectionProvider.initialize(PoolingConnectionProvider.java:210)
        at org.quartz.utils.PoolingConnectionProvider.<init>(PoolingConnectionProvider.java:155)
        at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1014)
        at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1519)
        at com.example.singleQuartz.SingleQuartzKeepAlive.main(SingleQuartzKeepAlive.java:17)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
    Caused by: java.lang.ClassNotFoundException: com.mchange.v2.c3p0.ComboPooledDataSource
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 10 more
    
    Process finished with exit code 1
    View Code

     运行结果正常,我们在数据库可以看到job和trigger表都有了任务调度记录数据。此时我们不妨只启动原来的调度器,看看是否任务调度还能正常进行。通过结果同学们就很清楚了,这次任务调度我们只执行了4次,因为在程序中断之前运行了7次。

    package com.example.singleQuartz;
    
    import org.quartz.Scheduler;
    import org.quartz.SchedulerException;
    import org.quartz.impl.StdSchedulerFactory;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by XuHui on 2016/12/23.
     */
    public class SingleQuartzOldScheduler {
        public static void main(String[] args) throws SchedulerException {
            StdSchedulerFactory factory = new StdSchedulerFactory();
            factory.initialize("quartz.properities");
            Scheduler scheduler = factory.getScheduler();
            scheduler.start();
            try{
                TimeUnit.MINUTES.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                scheduler.shutdown();
            }
        }
    }
    =================结果====================
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED started.
    Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:16 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:26 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:36 CST 2016 by DEFAULT.job
    Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:46 CST 2016 by DEFAULT.job
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED shutting down.
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED paused.
    [main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED shutdown complete.
    View Code

    5.最终的工程目录结构

    (三)Quartz集群

    1.搭建web工程

      看完第二部分之后,大部分同学已经明白了,Quartz集群是怎么回事了。其实就是把调度任务存到数据库中,集群的时候每台服务器调度任务都指向同一数据库,从同一数据库取调度任务,这就是Quartz集群了。为在本地验证quartz集群,我重新搭建一个工程,里面撘两个web模块,目录结构如下。clusterweb_two和clusterweb_one文件和配置一模一样。

    2.配置Quartz集群文件quartz.properities

      多台服务器上的这个配置文件除了instanceId不同,这里可以设置成AUTO根据机器自动生成,其他基本都相同,必须限定的是数据库信息必须相同。

    org.quartz.scheduler.instanceName = ClusterScheduler
    org.quartz.threadPool.threadCount = 3
    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.dataSource = myDS
    # Cluster
    org.quartz.jobStore.isClustered = true
    org.quartz.scheduler.instanceId = AUTO
    
    org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
    org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8
    org.quartz.dataSource.myDS.user = root
    org.quartz.dataSource.myDS.password = 123456
    org.quartz.dataSource.myDS.maxConnections = 5

    3.配置web监听器

      我们把调度器放到web监听器中运行,当web启动时会运行这个监听器,同时会启动调度器,监听器QuartzApplicationListener.java如下所示。

    package com.example.web;
    
    import org.quartz.*;
    import org.quartz.impl.StdSchedulerFactory;
    
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by XuHui on 2016/12/23.
     */
    public class QuartzApplicationListener implements ServletContextListener {
        private Scheduler scheduler = null;
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
            StdSchedulerFactory factory = new StdSchedulerFactory();
            try {
                factory.initialize("quartz.properities");
                scheduler = factory.getScheduler();
                 /* 启动调度器 */
                scheduler.start();
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
    
            JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                    .withIdentity("job", Scheduler.DEFAULT_GROUP)
                    .build();
    
            Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
            SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
                    .withIdentity("trigger", Scheduler.DEFAULT_GROUP)
                    .startAt(startTime)
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInSeconds(10)
                            .withRepeatCount(10))
                    .forJob(jobDetail)
                    .build();
            try{
                /* 交由调度器调度Job */
                scheduler.scheduleJob(jobDetail, simpleTrigger);
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
    
            /* 3分钟任务调度之后关闭调度器 */
            try{
                TimeUnit.MINUTES.sleep(3);
            }catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent servletContextEvent) {
            try{
                scheduler.shutdown();
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
        }
    }
    View Code

    4.web配置文件web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
        <listener>
            <listener-class>
                com.example.web.QuartzApplicationListener
            </listener-class>
        </listener>
    </web-app>

    5.运行两个tomcat模拟集群

      在idea中配置两个tomcat,并且让两个tomcat同时运行有点小麻烦,主要注意一点每个tomcat配置的Http Port 和 JMX Port要不同。

    tomcat1 : Http Port : 8081 JMX Port : 1099
    
    tomcat2 : Http Port : 8082 JMX Port : 1100

      tomcat1和tomcat2运行成功之后,此时两个服务都已启动,此时只有tomcat1开始任务调度,tomcat2未进行任务调度。首先把tomcat1服务停掉,此时会出现tomcat2任务调度开始,这里跟做服务高可靠性很像。运行结果:

    ==============tomcat1=====================
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:00 CST 2016 by DEFAULT.job on webone
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:10 CST 2016 by DEFAULT.job on webone
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:20 CST 2016 by DEFAULT.job on webone
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:30 CST 2016 by DEFAULT.job on webone
    D:work	omcatapache-tomcat-8.0.23-windows-x64apache-tomcat-8.0.23incatalina.bat stop
    Disconnected from server
    ==============tomcat2=====================
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:56 CST 2016 by DEFAULT.job on webtwo
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:56 CST 2016 by DEFAULT.job on webtwo
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:00 CST 2016 by DEFAULT.job on webtwo
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:10 CST 2016 by DEFAULT.job on webtwo
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:20 CST 2016 by DEFAULT.job on webtwo
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:30 CST 2016 by DEFAULT.job on webtwo
    Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:40 CST 2016 by DEFAULT.job on webtwo
    [2016-12-23 11:29:48,369] Artifact clusterweb_two:war exploded: Artifact is deployed successfully

    写在最后Quartz基础部分总结起来可能不多,实际应用主要是Job中execute方法的实现。看完上面的介绍之后我们发现,Quartz使用起来非常方便,Quartz提供了很多日期和日历类。写过定时备份、定时查询统计方法的同学,知道这里写起来很麻烦。Quqatz大大缩减了程序的代码量。之前和一位同事讨论了什么时候用Quartz,很多时候我们都会自己写任务调度。一般都是这样实现的:我们会启线程相当于定时器,定时去查询符合条件的Job,如果查询到多Job同时调度,我们会启一个线程池多线程并发运行。这样看上去Quartz都具备了,使用Quartz之后代码非常简洁、可复用性高并且任务调度的可持久性,只需要配置Quartz配置文件就可以将调度任务存到数据库中,正是因为这点所以Quartz可以集群,对于任务数很多的集群环境下,这就是无与伦比的优点了。如果在搭建环境或者发现文章中有不对或不足的地方,可以在下方留言,大家共同学习。

  • 相关阅读:
    2019天梯赛训练1
    Python课程设计 搭建博客
    最容易理解的贪吃蛇小游戏
    数据结构-队列
    数据结构-堆栈(2)
    数据结构-堆栈(1)
    数据结构-线性表(3)
    数据结构-线性表(1)
    linux知识积累
    Maven学习笔记
  • 原文地址:https://www.cnblogs.com/jave1ove/p/6212099.html
Copyright © 2020-2023  润新知