zoukankan      html  css  js  c++  java
  • Quartz总结

    前言

    最近项目中有使用到Quartz,得空便总结总结,顺便记录一下这种设计模式,毕竟“好记性不如烂笔头”。

    搭建

    pom文件:

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

    quartz.properties配置:

    org.quartz.scheduler.instanceName = MyScheduler
    org.quartz.threadPool.threadCount = 3
    org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

    简单的示例

    EnumCheckJobType,任务类型

    public enum EnumCheckJobType {
    
        ONE,TWO
        
    }

    SchedulerTask,任务的接口

    public interface SchedulerTask {
    
        void run();
    
    }

    OneScheduler,一个具体的任务

    public class OneScheduler implements SchedulerTask{
    
        @Override
        public void run() {
            //do something
        }
    }

    TwoScheduler,另一个具体的任务

    ublic class TwoScheduler implements SchedulerTask{
    
        @Override
        public void run() {
            //do something
        }
    }

    MyJob,执行任务(必须实现Job接口)

    public class MyJob implements Job {
    
        @Override
        public void execute(JobExecutionContext jobExecutionContext) {
            
            SchedulerTask task = (SchedulerTask) jobExecutionContext.getMergedJobDataMap().get("task");
            task.run();     
        }  
    }

    MyScheduler,调度器(什么时候执行什么任务,指的是MyJob的execute方法)

    public class MyScheduler {
    
        /**
         * 注册调度任务
         *
         * @param scheduler
         * @param jobType
         * @param cronExpress cron 表达式
         */
        public void scheduleJob(Scheduler scheduler, EnumCheckJobType jobType, String cronExpress) {
            try {
                scheduleJob(scheduler, jobType, CronScheduleBuilder.cronSchedule(cronExpress));
            } catch (SchedulerException e) {
    
            }
        }
    
    
        /**
         * 注册调度任务
         *
         * @param scheduler
         * @param jobType
         * @param interval  间隔时间
         */
        public void scheduleJob(Scheduler scheduler, EnumCheckJobType jobType, int interval) {
            try {
                scheduleJob(scheduler, jobType, SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(interval)
                        .repeatForever()
                        .withMisfireHandlingInstructionNowWithExistingCount());//misfire策略
            } catch (SchedulerException e) {
    
            }
        }
    
        /**
         * 注册调度任务
         *
         * @param scheduler
         * @param jobType
         * @param builder
         */
        public <T extends Trigger> void scheduleJob(Scheduler scheduler, EnumCheckJobType jobType, ScheduleBuilder<T> builder) throws SchedulerException {
    
            SchedulerTask task = newTask(jobType);
            if (task == null) {
                throw new SchedulerException();
            }
    
            JobDetail job = JobBuilder.newJob(MyJob.class)
                    .withIdentity(getJobName(jobType), getJobGroupName(jobType))
                    .build();
    
            job.getJobDataMap().put("task", task);
    
            // 第二天的零点开始执行
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(getJobTriggerName(jobType), getJobGroupName(jobType))
                    .forJob(job.getKey())
                    .withSchedule(builder)
                    .startAt(DateBuilder.tomorrowAt(0, 0, 0))
                    .build();
    
            scheduler.scheduleJob(job, trigger);
    
        }
    
    
        private String getJobName(EnumCheckJobType checkType) {
            return checkType.name();
        }
    
    
    
        private String getJobGroupName(EnumCheckJobType checkType) {
            return "group-" + checkType.name();
        }
    
    
        private String getJobTriggerName(EnumCheckJobType jobType) {
            return jobType.name();
        }
    
    
        private SchedulerTask newTask(EnumCheckJobType jobType) {
            switch (jobType) {
                case ONE:
                    return applicationContext.getBean
                            (OneScheduler.class);
                case TWO:
                    return applicationContext.getBean
                            (TwoScheduler.class);
                default:
                    return null;
            }
        }
    }

    HelloQuartz,主函数

    public class HelloQuartz {
        public static void main(String[] args) throws SchedulerException {
            MyScheduler myScheduler = new MyScheduler();
            try {
                Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
                myScheduler.scheduleJob(scheduler, EnumCheckJobType.ONE, "0 0 0 * * ?");//每天的凌晨0点执行
                myScheduler.scheduleJob(scheduler, EnumCheckJobType.TWO, 60);//间隔60S执行一次
           scheduler.start();//启动调度任务 } catch (SchedulerException e) { } } }

    Quartz的3个基本要素

    • Scheduler:调度器。所有的调度都是由它控制。
    • Trigger: 触发器。决定什么时候来执行任务。
    • JobDetail & Job: JobDetail 定义的是任务数据,而真正的执行逻辑是在Job中。使用JobDetail + Job而不是Job,这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。

    name和group

    JobDetail和Trigger都有name和group。

    name是它们在这个sheduler里面的唯一标识。如果我们要更新一个JobDetail定义,只需要设置一个name相同的JobDetail实例即可。

    group是一个组织单元,sheduler会提供一些对整组操作的API,比如 scheduler.resumeJobs()。

    misfire(错失触发)策略

    当系统由于某种原因(未启动或是没有可用线程)在预定时刻没有启动任务,之后当系统可以调度该任务时(系统启动或是取得了可用线程),会首先检查当前时刻与预定时刻的差值,如果小于等于misfireThreshold值(该参数缺省为60秒),则不认为发生Misfire,并立刻启动该任务,一切正常进行。如果大于misfireThreshold值,则认为发生了misfire,此时的行为由trigger的Misfire Instructions来决定。而不同类型的trigger的缺省Misfire Instructions是不同的。

    对于典型的SimpleTrigger: 

    缺省Misfire策略为Trigger.MISFIRE_INSTRUCTION_SMART_POLICY ,其他策略如下:

    SimpleScheduleBuilder ssb = SimpleScheduleBuilder.simpleSchedule();
    ssb.withMisfireHandlingInstructionFireNow();//1
    ssb.withMisfireHandlingInstructionIgnoreMisfires();//2
    ssb.withMisfireHandlingInstructionNextWithExistingCount();//3
    ssb.withMisfireHandlingInstructionNextWithRemainingCount();//4
    ssb.withMisfireHandlingInstructionNowWithExistingCount();//5
    ssb.withMisfireHandlingInstructionNowWithRemainingCount();//6
    
    //1
    withMisfireHandlingInstructionFireNow  ---> SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW (misfireInstruction == 1)
    ——以当前时间为触发频率立即触发执行
    ——执行至FinalTIme的剩余周期次数
    ——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
    ——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
    
    //2
    withMisfireHandlingInstructionIgnoreMisfires ---> Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY(misfireInstruction == -1)
    —以错过的第一个频率时间立刻开始执行
    ——重做错过的所有频率周期
    ——当下一次触发频率发生时间大于当前时间以后,按照Interval的依次执行剩下的频率
    ——共执行RepeatCount+1次
    
    //3
    withMisfireHandlingInstructionNextWithExistingCount ---> SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT(misfireInstruction == 5)
    ——不触发立即执行
    ——等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数
    ——以startTime为基准计算周期频率,并得到FinalTime
    ——即使中间出现pause,resume以后保持FinalTime时间不变
    
    //4
    withMisfireHandlingInstructionNextWithRemainingCount ---> SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT(misfireInstruction == 4)
    ——不触发立即执行
    ——等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数
    ——以startTime为基准计算周期频率,并得到FinalTime
    ——即使中间出现pause,resume以后保持FinalTime时间不变
    
    //5
    withMisfireHandlingInstructionNowWithExistingCount ---> SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT(misfireInstruction == 2)
    ——以当前时间为触发频率立即触发执行
    ——执行至FinalTIme的剩余周期次数
    ——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
    ——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
    
    //6
    withMisfireHandlingInstructionNowWithRemainingCount ---> SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT(misfireInstruction == 3)
    ——以当前时间为触发频率立即触发执行
    ——执行至FinalTIme的剩余周期次数
    ——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
    ——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值

    总结:
    1、带有_NOW字样的策略,就是立即执行;反之,带有_NEXT的策略,则会等到下一个触发周期才会执行。
    2、带有WITH_EXISTING_REPEAT_COUNT字样的,则是确保周期总数不变,用周期总数-已执行数作为剩余周期数,因此FinalTime会适当延后;
    例如,repeatCount为3次(总计4次),已执行1次,错过2次,则后续仍会执行4-1=3次。
    3、带有WITH_REMAINING_REPEAT_COUNT则是按原定计划执行,FinalTime不变,已错过的忽略。
    例如,repeatCount为3次(总计4次),已执行1次,错过2次,则后续会执行4-1-2=1次。

    基于在创建SimpleTrigger时选择的MISFIRE_INSTRUCTION_XXX更新SimpleTrigger的状态。 如果失火指令设置为MISFIRE_INSTRUCTION_SMART_POLICY,则将使用以下方案:

    • 如果重复计数为0,则指令将解释为MISFIRE_INSTRUCTION_FIRE_NOW。
    • 如果重复计数为REPEAT_INDEFINITELY(repeatForever),则指令将解释为MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT。 警告:如果触发器具有非空的结束时间,则使用MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT可能会导致触发器在失火时间范围内到达结束时,不会再次触发。
    • 如果重复计数大于0,则指令将解释为MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT。

    对于典型的CronTrigger: 

    缺省Misfire策略为Trigger.MISFIRE_INSTRUCTION_SMART_POLICY ,其他策略如下:

    CronScheduleBuilder csb = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
    csb.withMisfireHandlingInstructionDoNothing();
    csb.withMisfireHandlingInstructionFireAndProceed();
    csb.withMisfireHandlingInstructionIgnoreMisfires();
    
    withMisfireHandlingInstructionDoNothing ---> CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING(misfireInstruction = 2)
    ——不触发立即执行
    ——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
    
    withMisfireHandlingInstructionFireAndProceed ---> CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW(misfireInstruction = 1)
    ——以当前时间为触发频率立刻触发一次执行
    ——然后按照Cron频率依次执行
    
    withMisfireHandlingInstructionIgnoreMisfires ---> Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY(misfireInstruction = -1)
    ——以错过的第一个频率时间立刻开始执行
    ——重做错过的所有频率周期后
    ——当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行

    根据创建CronTrigger时选择的MISFIRE_INSTRUCTION_XXX更新CronTrigger的状态。 如果失火指令设置为MISFIRE_INSTRUCTION_SMART_POLICY,则将使用以下方案:

    • 指令将解释为MISFIRE_INSTRUCTION_FIRE_ONCE_NOW

    参考

    这里有一篇博客总结的非常好,记录并分享下

    http://www.cnblogs.com/drift-ice/p/3817269.html

                                         

     

  • 相关阅读:
    Constants and Variables
    随想
    C#基础篇之语言和框架介绍
    Python基础19 实例方法 类方法 静态方法 私有变量 私有方法 属性
    Python基础18 实例变量 类变量 构造方法
    Python基础17 嵌套函数 函数类型和Lambda表达式 三大基础函数 filter() map() reduce()
    Python基础16 函数返回值 作用区域 生成器
    Python基础11 List插入,删除,替换和其他常用方法 insert() remove() pop() reverse() copy() clear() index() count()
    Python基础15 函数的定义 使用关键字参数调用 参数默认值 可变参数
    Python基础14 字典的创建修改访问和遍历 popitem() keys() values() items()
  • 原文地址:https://www.cnblogs.com/irain/p/7458223.html
Copyright © 2011-2022 走看看