quartz定时任务不执行(任务统筹策略)

由于某种原因,例如应用停掉,导致定时任务错过了本该执行的时间点,这就是定时任务过期。对于过期的定时任务,我们需要基于某种策略对其进行处理。过期策略在Trigger接口中定义了两种过期策略,publicstaticfinalintMISFIRE_INSTRUCTION_SMART_POLICY=0;publicstaticfinalintMISF…

大家好,又见面了,我是你们的朋友全栈君。

由于某种原因,例如应用停掉,导致定时任务错过了本该执行的时间点,这就是定时任务过期。对于过期的定时任务,我们需要基于某种策略对其进行处理。

过期策略

在Trigger接口中定义了两种过期策略,

    public static final int MISFIRE_INSTRUCTION_SMART_POLICY = 0;
    
    public static final int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1;

MISFIRE_INSTRUCTION_SMART_POLICY 表明过期的定时任务将使用智能策略,如何处理将依赖于具体的Trigger实现类。等会我们会专门分析最常用的CronTrigger的智能过期策略的实现。
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY 表明对于过期的定时任务将不执行任何过期策略。

设定过期策略

以CronTrigger为例,我们在添加定时任务的时候可以通过CronSchedulerBuilder设定过期策略。

 JobDetail jobDetail = JobBuilder.newJob(PrintJob.class)
                .storeDurably(true)
                .withIdentity(id)
                .withDescription(desc)
                .setJobData(jobDataMap).build();

CronScheduleBuilder schBuilder = CronScheduleBuilder.cronSchedule(cron)
.withMisfireHandlingInstructionFireAndProceed();

Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(id)
                .withDescription(desc)
                .forJob(jobDetail)
                .withSchedule(schBuilder).build();

如果不指定默认就是MISFIRE_INSTRUCTION_SMART_POLICY ,即采用智能策略模式。

CronTrigger过期策略

CronTrigger的智能过期策略有两种

public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;
public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2;

看字面意思很好理解,第一种是立即触发一次,第二种是忽略掉不管。定时任务过期后,需要对triiger进行处理,具体的处理是在具体的Trigger实现类中实现的,例如CronTriggerImpl

@Override
    public void updateAfterMisfire(org.quartz.Calendar cal) { 
   
        int instr = getMisfireInstruction();

        if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY)
            return;

        if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) { 
   
            instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
        }

        if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) { 
   
            Date newFireTime = getFireTimeAfter(new Date());
            while (newFireTime != null && cal != null
                    && !cal.isTimeIncluded(newFireTime.getTime())) { 
   
                newFireTime = getFireTimeAfter(newFireTime);
            }
            setNextFireTime(newFireTime);
        } else if (instr == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) { 
   
            setNextFireTime(new Date());
        }
    }

从源码中可以看到,CronTrigger的默认智能策略就是立即触发一次(MISFIRE_INSTRUCTION_FIRE_ONCE_NOW),即更新下次触发时间为当前时间。

过期定时任务检测

前半部分我们讲了定时任务的过期策略,并以CronTrigger为例,说明了其智能过期策略是如何实现的。后半部分,我们将视角从Trigger提升到Scheduler,看看Quartz是如何检测出过期定时任务的。

在JobStoreSupport中有一个叫MisfireHandler的线程,它负责定期检测出那些过期的定时任务。我们来看看它是如何做的。

首先,我们要明确一个过期的标准。一个比较直观的想法就是,如果一个Trigger的下次触发时间在当前时间之前,那么这个定时任务肯定是过期了。但是我们为了给人家一个机会,可以通过减去一个值,稍稍把这个截止时间调前一点。这个值可以通过quartz.properties配置,默认是60000,即60秒。

org.quartz.jobStore.misfireThreshold: 10000

当然Trigger必须是WAITING状态下才行,非WAITING状态的Trigger表明定时任务正在处理,已经完成或者出错了。而且过期策略不是MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY 。

有了这三个过期指标,过期的定时任务就好筛选了。我们去QRTZ_TRIGGERS表里面,把符合这三个条件的Trigger都筛选出来。

过期定时任务恢复

以MISFIRE_INSTRUCTION_FIRE_ONCE_NOW过期策略为例,拿到过期的Trigger后,根据过期策略,调用updateAfterMisfire()对其进行更新,包括设置下次触发时间,更新状态为WAITING。

处理完trigger后,MisfireHandler会立即向QuartzSchedulerThread发送信号,告诉有紧急任务达到。

if (recoverMisfiredJobsResult.getProcessedMisfiredTriggerCount() > 0) { 
   
          
  signalSchedulingChangeImmediately(recoverMisfiredJobsResult.getEarliestNewTime());
}

QuartzSchedulerThread接收到信号后,如果才刚刚开始扫描触发器则必定能扫描到这个触发器,所以不用做任何额外处理。如果已经获取过触发器了,则释放掉已经获取的触发器,重新开始扫描。

if(releaseIfScheduleChangedSignificantly(triggers, triggerTime)) { 
   
   break;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/128399.html原文链接:https://javaforall.net

(1)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • subdiscipline_Sublime

    subdiscipline_Sublime部分转载自:使用sublime-snippet来快速做前端页面分析在sublime来中,可以通过submlime-snippet来快速补全代码。举个栗子,如果在sublime的存放submlime-snippet的文件夹下有如下的文件(elem-edge.sublime-snippet文件名不重要)ele

    2025年6月13日
    6
  • python写helloworld代码_helloworld代码怎么写?

    python写helloworld代码_helloworld代码怎么写?展开全部java:java”>publicclasshelloworld{publicstaticvoidmain(String[]args){System.out.println(“Helloworld!”);}}python2:print”Helloworld!”python3:print(“Helloworld!”)C/C++:#includeintmain(…

    2022年7月16日
    19
  • Android HandlerThread 详解

    Android HandlerThread 详解概述HandlerThread相信大家都比较熟悉了,从名字上看是一个带有Handler消息循环机制的一个线程,比一般的线程多了消息循环的机制,可以说是Handler+Thread的结合

    2022年6月30日
    24
  • jdbc连接数据库的基本步骤「建议收藏」

    jdbc连接数据库的基本步骤「建议收藏」1.在项目中导入java.sql包2.加载数据库驱动程序Class.forName(“com.mysql.cj.jdbc.Driver”);3.定义数据库的链接地址Stringurl=“jdbc:mysql://localhost/studentserverTimezone=GMT%2B8&useSSL=false”;Stringdatabasename=“root”;S…

    2022年7月15日
    18
  • 计算机负数补码_负数用补码表示如何理解

    计算机负数补码_负数用补码表示如何理解在计算机系统中,数值一律用补码来表示(存储)。主要原因:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。2、补码与原码的转换过程几乎是相同的。数值的补码表示也分两种情况:(1)正数的补码:与原码相同。例如,+9的补码是00001001。(2)负数的补码:符号位为1

    2025年6月13日
    3
  • clion永久激活码2021【在线注册码/序列号/破解码】「建议收藏」

    clion永久激活码2021【在线注册码/序列号/破解码】,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月18日
    292

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号