运行错过的Quartz作业

时间:2011-11-23 17:19:39

标签: java spring groovy scheduled-tasks quartz-scheduler

我正在使用Quartz with Spring在每个月的第一天午夜运行特定任务。我通过设置我的服务器日期和时间测试了这项工作。这个月的最后一天是11:59的时间,启动服务器并在转向12:00时观察任务运行,但我担心服务器(无论出于何种原因)可能无法运行的情况这个月的第一天午夜。

我认为Quartz中的失火处理会关心这一点,但也许我错了?

任何人都可以告诉我如何处理这个问题吗?我真的不想创建一个每隔'x'秒/分钟/小时运行一次的工作,如果可以避免的话,检查一下我是否需要运行这份工作。

我也很好奇为什么我没有看到任何与Quartz相关的日志信息,但这是次要问题。

这是我的任务弹簧配置:

<bean id="schedulerService" class="com.bah.pams.service.scheduler.SchedulerService">
    <property name="surveyResponseDao" ref="surveyResponseDao"/>
    <property name="organizationDao" ref="organizationDao"/>
</bean>

<bean name="createSurveyResponsesJob" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="com.bah.pams.service.scheduler.jobs.CreateSurveyResponsesJob"/>
    <property name="jobDataAsMap">
        <map>
            <entry key="schedulerService" value-ref="schedulerService"/>
        </map>
    </property>
</bean>
<!-- Cron Trigger -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="createSurveyResponsesJob"/>
    <property name="cronExpression" value="0 0 0 1 * ? *"/>
    <!--if the server is down at midnight on 1st of month, run this job as soon as it starts up next -->
    <property name="misfireInstructionName" value="MISFIRE_INSTRUCTION_FIRE_ONCE_NOW"/>
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

    <property name="autoStartup" value="true"/>

    <property name="quartzProperties">
        <props>
            <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
            <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
        </props>
    </property>
    <property name="jobDetails">
        <list>
            <ref bean="createSurveyResponsesJob"/>
        </list>
    </property>

    <property name="triggers">
        <list>
            <ref bean="cronTrigger"/>
        </list>
    </property>
</bean>

2 个答案:

答案 0 :(得分:2)

MISFIRE_INSTRUCTION_FIRE_ONCE_NOW就是您提到的目的,如果您怀疑服务器已关闭,您肯定应该将作业从JVM内存中保留下来(例如:使用JDBCJobStore而不是RAMJobStore)。

RAMJobStore快速且轻量级,但当进程终止时,所有调度信息都会丢失。

http://quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigRAMJobStore

JDBCJobStore用于在关系数据库中存储调度信息(作业,触发器和日历)。

http://quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigJobStoreTX

希望有所帮助。

答案 1 :(得分:0)

我也遇到过想要在服务器重启时运行最后一个计划作业的问题。

这是我发现的解决方案是返回触发器的一个时间间隔并计算下一个触发时间。通过迭代所有触发器,可以确定触发器过去应该触发的最近时间。


计算每次射击之间的间隔:

Date nextFireTime = trigger.getNextFireTime();
Date subsequentFireTime = trigger.getFireTimeAfter(nextFireTime);
long interval = subsequentFireTime.getTime() - nextFireTime.getTime();

找到下一次的射击时间,直到过去的时间间隔:

Date previousPeriodTime = new Date(System.currentTimeMillis() - interval);
Date previousFireTime = trigger.getFireTimeAfter(previousPeriodTime);

我发现如果您使用的是CronTrigger,这可以防止您过去要求过火时间。要解决此问题,我修改了开始时间,因此上面的代码段变为:

Date originalStartTime = trigger.getStartTime(); // save the start time
Date previousPeriodTime = new Date(originalStartTime.getTime() - interval);
trigger.setStartTime(previousPeriodTime);
Date previousFireTime = trigger.getFireTimeAfter(previousPeriodTime);
trigger.setStartTime(originalStartTime); // reset the start time to be nice

遍历所有触发器,找到过去最近的触发器:

for (String groupName : scheduler.getTriggerGroupNames()) {
    for (String triggerName : scheduler.getTriggerNames(groupName)) {
        Trigger trigger = scheduler.getTrigger(triggerName, groupName);
        // code as detailed above...
        interval = ...
        previousFireTime = ...
    }
}

我会将它作为练习留给读者,将其重构为辅助方法或类。实际上,我在子类委托触发器中使用上述算法,然后将其置于按先前触发时间排序的集合中。