• Quartz的学习记录


    首先写一个Quartz的简单例子:

    1.新建一个maven项目,pom.xml中加入quartz的依赖:

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

    2.新建quartz.properties文件,放在classpath下,添加如下配置:

    #此调度程序的名称将为“MyScheduler”
    org.quartz.scheduler.instanceName = MyScheduler

    #线程池中有3个线程,这意味着最多可以同时运行3个job。
    org.quartz.threadPool.threadCount = 3

    #quartz的所有数据,包括job和trigger的配置,都会存储在内存中(而不是数据库里)
    org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

    3.新建一个类继承org.quartz.Job:

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

    import java.text.SimpleDateFormat;
    import java.util.Date;

    public class QuartzJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    System.out.println(sdf.format(date));
    }
    }

    4.创建Scheduler执行任务:

    import com.quartzdemo.QuartzJob;
    import org.quartz.JobDetail;
    import org.quartz.Scheduler;
    import org.quartz.SchedulerException;
    import org.quartz.Trigger;
    import org.quartz.impl.StdSchedulerFactory;

    import static org.quartz.JobBuilder.newJob;
    import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
    import static org.quartz.TriggerBuilder.newTrigger;

    public class QuartzTest {
    public static void main(String[] args) {
    try {
    Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    scheduler.start();
    JobDetail job = newJob(QuartzJob.class).withIdentity("job1","group1").build();
    Trigger trigger = newTrigger().withIdentity("trigger1","group1")
    .startNow()
    .withSchedule(simpleSchedule()
    .withIntervalInSeconds(1)
    .repeatForever())
    .build();
    scheduler.scheduleJob(job,trigger);
    // Thread.sleep(6000);可以指定执行多长时间后关闭Scheduler
    // scheduler.shutdown();关闭Scheduler
    } catch (SchedulerException | InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    该程序会一直执行,也可以通过设置一定时间后结束执行。

    5.执行结果:

     ...

     下面学习Quartz中比较常用到的组件:

    1.Job表示一个工作,要执行的具体内容。此接口中只有一个方法,就是execute()。

       每当Scheduler执行job时,在调用其execute()方法之前会创建该类的一个新的实例,执行完后该实例就会被丢弃,被垃圾回收。

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

    3.Trigger是一个调度参数的设置,什么时候去调以及多久调一次。

     Quartz自带了各种不同类型的Trigger,最常用的主要是SimpleTrigger和CronTrigger。

        SimpleTrigger主要用来设置在特定时间点执行一次任务,重复执行N次,每次执行间隔T个时间单位。

        CronTrigger在基于日历的调度上执行,比如在每月的第十天执行,或者每个星期5的上午执行。

       Trigger的公共属性有:

         jobKey(当trigger触发时执行的job身份),startTime(设置trigger第一次执行的时间点),endTime(表示trigger失效的时间点)

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

       Scheduler可以启动(start),暂停(stand-by)和停止(shutdown)。Scheduler创建后,可以增加,删除和列举Job和Trigger,以及执行其他和调度相关的工作(如:暂停trigger)

     5.JobBuilder用于定义、构建JobDetail的实例,用于定义作业的实例

    6.TriggerBuilder用于定义、构建Trigger的实例。

    Key

    将Job和Trigger注册到一个Scheduler时,可以为他们设置key。key由name和group组成,同一个分组下的Job和Trigger的名称必须唯一。

    newJob(QuartzJob.class).withIdentity("job1","group1")
    newTrigger().withIdentity("trigger1","group1")

    JobDataMap

    前面提到,每次执行execute时,都会新建一个job类,执行完后,该实例被垃圾回收。那么如何追踪Job执行的状态,执行的进度或者执行的结果呢?答案就是JobDataMap,JobDetail的一部分。

    JobDataMap可以包含不限量(序列化)的数据对象,Job执行的时候可以获取其中的数据。

    下面就是Job存取JobDataMap数据的例子:

    import org.quartz.*;

    public class UsingDataMapJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();

         //在Job执行过程中可以把数据从JobDataMap中取出来
    String name = jobDataMap.getString("name");
    String gender = jobDataMap.getString("gender");

    System.out.println(name+":"+gender);
    }
    }
    ********************************************************************************************************
    import org.quartz.*;
    import org.quartz.impl.StdSchedulerFactory;
    public class QuartzJobDataMapDemo {
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        //在构建JobDetail的时候,把数据放入JobDataMap
    JobDetail jobDetail = JobBuilder.newJob(UsingDataMapJob.class)
    .usingJobData("name","jenny")
    .usingJobData("gender","female")
    .withIdentity("job1","group1")
    .build();

    Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1","group1")
    .startNow()
    .withSchedule(SimpleScheduleBuilder.simpleSchedule())
    .build();

    Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    scheduler.start();
    scheduler.scheduleJob(jobDetail,trigger);

    // Thread.sleep(2000);
    // scheduler.shutdown();
    }
    }

     需要注意的是,如果是JobDataMap是持久化的存储机制(JobStore),JobDataMap中存储的对象会被序列化。所以就有可能会遇到存储完后,该类后续又发生变化,导致类的版本不一致的问题。也可以配置JDBC-JobStore和JobDataMap来限制map中只能存储基本类型和String类型。

    JobExecutionContext 中的JobDataMap是JobDetail和Trigger中的DataMap的并集,如果有相同的key,则后者会把前者覆盖。

    Trigger的misfire机制(https://www.cnblogs.com/skyLogin/p/6927629.html)

    misfire产生的条件是:到了该触发执行时上一个执行还未完成,且线程池中没有空闲线程可以使用(或有空闲线程可以使用但job设置为@DisallowConcurrentExecution)且过期时间已经超过misfireThreshold就认为是misfire了,错失触发了。

    比如:13:07:24开始执行,重复执行5次,开始执行时,quartz已经计算好每次调度的时间刻,分别如下:

    03:33:36,03:33:39,03:33:42,03:33:45,03:33:48,03:33:51

    如果第一次执行时间为11s,到03:33:47结束,03:33:47减去03:33:39的时间间隔是8s,如果misfireThreshold设置的时间小于等于8s间隔,则认为是misfire了,如果大于8s间隔,则认为没有misfire。

    SimpleTrigger的MisFire策略:

    MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY

    MISFIRE_INSTRUCTION_FIRE_NOW

    MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT

    MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT

    MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT

    MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT

    CronTrigger的MisFire策略:

    MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY

    MISFIRE_INSTRUCTION_DO_NOTHING

    MISFIRE_INSTRUCTION_FIRE_NOW

    日历示例Calendar:Calendar用于从trigger指定的执行计划中排除时间段,比如trigger计划每天的上午10点执行任务,可以另外设置一个Calendar来排除国家节假日。

    CronExpressions(http://www.quartz-scheduler.org/api/2.3.0/index.html)

    Cron expressions are comprised of 6 required fields and one optional field separated by white space. The fields respectively are described as follows:

    Field Name Allowed Values Allowed Special Characters
    Seconds   0-59   , - * /
    Minutes   0-59   , - * /
    Hours   0-23   , - * /
    Day-of-month   1-31   , - * ? / L W
    Month   0-11 or JAN-DEC   , - * /
    Day-of-Week   1-7 or SUN-SAT   , - * ? / L #
    Year (Optional)   empty, 1970-2199   , - * /

     所有字段都有一组可以指定的有效值。这些值应该是相当明显的 - 例如秒和分钟的数字0到59,数小时的值0到23。日期可以是1-31的任何值,但是您需要注意在给定的月份中有多少天!月份可以指定为0到11之间的值,或者使用字符串JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV和DEC。星期几可以指定为1到7(1 =星期日)之间的值,或者使用字符串SUN,MON,TUE,WED,THU,FRI和SAT。

    CronTrigger例子:

    import com.quartzdemo.QuartzJob;
    import org.quartz.*;
    import org.quartz.impl.StdSchedulerFactory;

    public class CronTriggerDemo {
    public static void main(String[] args) {
    try {
    JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class).withIdentity("job1", "group1").build();
    Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 16-17 ? * 3")
    .withMisfireHandlingInstructionFireAndProceed())
    .build();
    Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    scheduler.start();
    scheduler.scheduleJob(jobDetail,trigger);
    } catch (SchedulerException e) {
    e.printStackTrace();
    }
    }
    }

    Listener:

    要创建一个listener,只需创建一个实现org.quartz.TriggerListener和/或org.quartz.JobListener接口的对象。然后,listener在运行时会向调度程序注册,并且必须给出一个名称(或者,他们必须通过他们的getName()方法来宣传自己的名字)。

      JobListener例子:

    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.quartz.JobListener;

    public class MyJobListener implements JobListener {
    @Override
    public String getName() {
    return "MyJobListener";
    }

    @Override
    public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
    System.out.println("Job 要被执行。");
    }

    @Override
    public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
    System.out.println("Job 执行被否决。");
    }

    @Override
    public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
    System.out.println("Job 已经被执行");
    }
    }
    ************************************************************************************
    public class CronTriggerDemo {
    public static void main(String[] args) {
    try {
    JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class).withIdentity("job1", "group1").build();
    Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("* * * 12 2 ?")
    // .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 16-17 ? * 3")
    .withMisfireHandlingInstructionFireAndProceed())
    .build();
    Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    scheduler.start();
    scheduler.getListenerManager().addJobListener(new MyJobListener(),KeyMatcher.keyEquals(new JobKey("job1", "group1")));
    scheduler.scheduleJob(jobDetail,trigger);
    } catch (SchedulerException e) {
    e.printStackTrace();
    }
    }
    }

    执行结果:

    Quartz的运行环境


    • Quartz 可以运行嵌入在另一个独立式应用程序。
    • Quartz 可以在应用程序服务器(或 servlet 容器)内被实例化,并且参与 XA 事务。
    • Quartz 可以作为一个独立的程序运行(其自己的 Java 虚拟机内),可以通过 RMI 使用。
    • Quartz 可以被实例化,作为独立的项目集群(负载平衡和故障转移功能),用于作业的执行。





  • 相关阅读:
    一个SQL语句实现的统计功能
    VS2005中的全角BUG(C#代码)[转]
    Observer Pattern(观察者模式)及其在C#中的实现
    我觉得VS2003中最差的地方
    上班了,有点困:(
    GPRS
    今天是郁闷的一天
    今天上午给公司老总演示了SharePoint项目的产品雏形
    介绍一下SharePoint
    SharePoint Service里面的东东真让人头疼
  • 原文地址:https://www.cnblogs.com/aliceQin/p/12294533.html
Copyright © 2020-2023  润新知