• Quartz使用总结


    Quartz可以用来做什么?

    Quartz是一个任务调度框架。比如你遇到这样的问题

    • 想每月25号,信用卡自动还款
    • 想每年4月1日自己给当年暗恋女神发一封匿名贺卡
    • 想每隔1小时,备份一下自己的爱情动作片 学习笔记到云盘

    这些问题总结起来就是:在某一个有规律的时间点干某件事。并且时间的触发的条件可以非常复杂(比如每月最后一个工作日的17:50),复杂到需要一个专门的框架来干这个事。 Quartz就是来干这样的事,你给它一个触发条件的定义,它负责到了时间点,触发相应的Job起来干活。

    一个简单的示例

    这里面的所有例子都是基于Quartz 2.2.1

    public class QuartzTest {
        public static void main(String[] args) {
            Quart(HelloQuartz.class,"*/1 * * * * ?");
        }
    
    
        private static void Quart(Class<? extends Job> clazz,String cron) {
            try {
                //创建scheduler
                Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    
                //定义一个Trigger
                Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") //定义name/group
                        .startNow()//一旦加入scheduler,立即生效
    //                    .withSchedule(simpleSchedule().withIntervalInSeconds(1).repeatForever()) //使用SimpleTrigger,每隔一秒执行一次,一直执行,奔腾到老不停歇
                        .withSchedule(CronScheduleBuilder.cronSchedule(cron))
                        .build();
    
                //定义一个JobDetail
                JobDetail job = JobBuilder.newJob(clazz) //定义Job类为HelloQuartz类,这是真正的执行逻辑所在
                        .withIdentity("job1", "group1") //定义name/group
                        .usingJobData("name", "Quart") //定义属性
                        .build();
    
                //加入这个调度
                scheduler.scheduleJob(job, trigger);
    
                //启动之
                scheduler.start();
    
                //运行一段时间后关闭
                Thread.sleep(10000);
                scheduler.shutdown(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
    }
    public class HelloQuartz implements Job {
        public void execute(JobExecutionContext context) throws JobExecutionException {
            JobDetail detail = context.getJobDetail();
            String name = detail.getJobDataMap().getString("name");
            System.out.println("say hello to " + name + " at " + new Date());
        }
    }

    个例子很好的覆盖了Quartz最重要的3个基本要素:

    • Scheduler:调度器。所有的调度都是由它控制。
    • Trigger: 定义触发的条件。例子中,它的类型是SimpleTrigger,每隔1秒中执行一次(什么是SimpleTrigger下面会有详述)。
    • JobDetail & Job: JobDetail 定义的是任务数据,而真正的执行逻辑是在Job中,例子中是HelloQuartz。 为什么设计成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()。

    Trigger

    在开始详解每一种Trigger之前,需要先了解一下Trigger的一些共性。

    StartTime & EndTime

    startTime和endTime指定的Trigger会被触发的时间区间。在这个区间之外,Trigger是不会被触发的。

    ** 所有Trigger都会包含这两个属性 **

    优先级(Priority)

    当scheduler比较繁忙的时候,可能在同一个时刻,有多个Trigger被触发了,但资源不足(比如线程池不足)。那么这个时候比剪刀石头布更好的方式,就是设置优先级。优先级高的先执行。

    需要注意的是,优先级只有在同一时刻执行的Trigger之间才会起作用,如果一个Trigger是9:00,另一个Trigger是9:30。那么无论后一个优先级多高,前一个都是先执行。

    优先级的值默认是5,当为负数时使用默认值。最大值似乎没有指定,但建议遵循Java的标准,使用1-10,不然鬼才知道看到【优先级为10】是时,上头还有没有更大的值。

    Misfire(错失触发)策略

    类似的Scheduler资源不足的时候,或者机器崩溃重启等,有可能某一些Trigger在应该触发的时间点没有被触发,也就是Miss Fire了。这个时候Trigger需要一个策略来处理这种情况。每种Trigger可选的策略各不相同。

    这里有两个点需要重点注意:

    • MisFire的触发是有一个阀值,这个阀值是配置在JobStore的。比RAMJobStore是org.quartz.jobStore.misfireThreshold。只有超过这个阀值,才会算MisFire。小于这个阀值,Quartz是会全部重新触发。

    所有MisFire的策略实际上都是解答两个问题:

    1. 已经MisFire的任务还要重新触发吗?
    2. 如果发生MisFire,要调整现有的调度时间吗?

    比如SimpleTrigger的MisFire策略有:

    • MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY

      这个不是忽略已经错失的触发的意思,而是说忽略MisFire策略。它会在资源合适的时候,重新触发所有的MisFire任务,并且不会影响现有的调度时间。

      比如,SimpleTrigger每15秒执行一次,而中间有5分钟时间它都MisFire了,一共错失了20个,5分钟后,假设资源充足了,并且任务允许并发,它会被一次性触发。

      这个属性是所有Trigger都适用。

    • MISFIRE_INSTRUCTION_FIRE_NOW

      忽略已经MisFire的任务,并且立即执行调度。这通常只适用于只执行一次的任务。

    • MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT

      将startTime设置当前时间,立即重新调度任务,包括的MisFire的

    • MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT

      类似MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT,区别在于会忽略已经MisFire的任务

    • MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT

      在下一次调度时间点,重新开始调度任务,包括的MisFire的

    • MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT

      类似于MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT,区别在于会忽略已经MisFire的任务。

    • MISFIRE_INSTRUCTION_SMART_POLICY

      所有的Trigger的MisFire默认值都是这个,大致意思是“把处理逻辑交给聪明的Quartz去决定”。基本策略是,

      1. 如果是只执行一次的调度,使用MISFIRE_INSTRUCTION_FIRE_NOW
      2. 如果是无限次的调度(repeatCount是无限的),使用MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
      3. 否则,使用MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT

    MisFire的东西挺繁杂的,可以参考这篇

    参考:https://www.cnblogs.com/drift-ice/p/3817269.html

  • 相关阅读:
    [转载]游戏外挂原理
    python cookbook
    RF user guide
    测试理论-selenium的工作原理
    测试理论- the conten of test plan
    测试理论--branch testing and boundary testing
    测试理论--向高级出发
    fiddler安装及配置
    python 面试题: 列表表达式
    [redis]如何将redis设置成diango的cache backend
  • 原文地址:https://www.cnblogs.com/heqiyoujing/p/11125935.html
Copyright © 2020-2023  润新知