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)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • html遮罩层样式,遮罩层样式

    .shade{width:100%;height:100%;position:absolute;top:0px;left:0px;z-index:5000;background:#000;opacity:0.7;}要遮罩的内容中还有下拉框,不用iframe的话,盖不住下拉框。使用了宽和高都为100%的iframe后,用了后会导致背景色和文字颜色等失效。//隐藏sele…

    2022年4月13日
  • 以太坊矿机组装教程_eth矿机组装

    以太坊矿机组装教程_eth矿机组装以太坊挖矿矿机组装指南伴随着比特币的热潮,“矿机”一词出现在了大众的视野中。那么何为“矿机”呢?一个矿机的本质就是一个电脑机箱,它的硬件组成与普通的台式机箱几乎相同。有区别的是它的主板上PCIE插槽比较多,电源的功率比较大。在这里简单解释下为什么会有这两点不同:1.PCIE插槽越多可连接的显卡就越多,单个矿机的算力就越大。2.电源功率越大能带动的显卡越多。所以大家在选择配置时一定要根据你的显卡…

    2022年9月30日
  • 我为什么放弃Go语言

    我为什么放弃Go语言我为什么放弃Go语言?有好几次,当我想起来的时候,总是会问自己:这个决定是正确的吗?是明智和理性的吗?其实我一直在认真思考这个问题。开门见山地说,我当初放弃Go语言,就是因为两个“不爽”:第一,对Go语言本身不爽;第二,对Go语言社区里的某些人不爽。毫无疑问,这是非常主观的结论,但是我有足够详实的客观的论据。

    2022年6月30日
  • JDK 安装 Java环境变量配置 详细教程「建议收藏」

    1、JDKjre-8u161-windows-x64.exe下载所有平台:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html选择“AcceptLicenseAgreement”根据系统选择对应的安装文件,64位系统安装64位JDK…

    2022年4月7日
  • 第五十九届冠军(使用C++设计一个类不能被继承)

    第五十九届冠军(使用C++设计一个类不能被继承)

    2022年1月7日
  • windows计算哈希值_哈希校验

    windows计算哈希值_哈希校验哈希算法校验

    2022年9月13日

发表回复

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

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