繁体   English   中英

如何在运行时更改 Spring 的 @Scheduled fixedDelay?

[英]How to change Spring's @Scheduled fixedDelay at runtime?

我需要以固定的时间间隔运行批处理作业,并且能够在运行时更改此批处理作业的时间。 为此,我遇到了 Spring 框架下提供的@Scheduled注释。 但我不确定如何在运行时更改 fixedDelay 的值。 我做了一些谷歌搜索,但没有找到任何有用的东西。

在 spring boot 中,您可以直接使用应用程序属性!

例如:

@Scheduled(fixedDelayString = "${my.property.fixed.delay.seconds}000")
private void process() {
    // your impl here
}

请注意,如果未定义属性,您也可以使用默认值,例如默认值为“60”(秒):

@Scheduled(fixedDelayString = "${my.property.fixed.delay.seconds:60}000")

我发现的其他事情:

  • 该方法必须为空
  • 该方法必须没有参数
  • 该方法可能是private

我发现能够方便地使用private可见性并以这种方式使用它:

@Service
public class MyService {
    public void process() {
        // do something
    }

    @Scheduled(fixedDelayString = "${my.poll.fixed.delay.seconds}000")
    private void autoProcess() {
        process();
    }
}

作为private ,计划方法可以是您服务的本地方法,而不是成为您服务 API 的一部分。

此外,这种方法允许process()方法返回一个值,而@Scheduled方法可能不会。 例如,您的process()方法可能如下所示:

public ProcessResult process() {
    // do something and collect information about what was done
    return processResult; 
}

提供有关处理过程中发生的事情的一些信息。

您可以使用Trigger来动态设置下一次执行时间。 在这里看到我的回答:

以编程方式使用 Spring 调度作业(使用 fixedRate 动态设置)

创建 interface ,类似这样:

    public abstract class DynamicSchedule{
        /**
         * Delays scheduler
         * @param milliseconds - the time to delay scheduler.
         */
        abstract void delay(Long milliseconds);

        /**
         * Decreases delay period
         * @param milliseconds - the time to decrease delay period.
         */
        abstract void decreaseDelayInterval(Long milliseconds);

        /**
         * Increases delay period
         * @param milliseconds - the time to increase dela period
        */
        abstract void increaseDelayInterval(Long milliseconds);
}

接下来,让我们实现位于 spring-context 项目中 org.springframework.scheduling 的 Trigger 接口。

import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;

import java.util.Date;
import java.util.concurrent.ScheduledFuture;

public class CustomDynamicSchedule extends DynamicSchedule implements Trigger {

    private TaskScheduler taskScheduler;
    private ScheduledFuture<?> schedulerFuture;

    /**
     * milliseconds
     */
    private long delayInterval;

    public CustomDynamicSchedule(TaskScheduler taskScheduler) {
        this.taskScheduler = taskScheduler;
    }


    @Override
    public void increaseDelayInterval(Long delay) {
        if (schedulerFuture != null) {
            schedulerFuture.cancel(true);
        }
        this.delayInterval += delay;
        schedulerFuture = taskScheduler.schedule(() -> { }, this);
    }

    @Override
    public void decreaseDelayInterval(Long delay) {
        if (schedulerFuture != null) {
            schedulerFuture.cancel(true);
        }
        this.delayInterval -= delay;
        schedulerFuture = taskScheduler.schedule(() -> { }, this);
    }

    @Override
    public void delay(Long delay) {
        if (schedulerFuture != null) {
            schedulerFuture.cancel(true);
        }
        this.delayInterval = delay;
        schedulerFuture = taskScheduler.schedule(() -> { }, this);
    }

    @Override
    public Date nextExecutionTime(TriggerContext triggerContext) {
        Date lastTime = triggerContext.lastActualExecutionTime();
        return (lastTime == null) ? new Date() : new Date(lastTime.getTime() + delayInterval);
    }
}

现在配置:

@Configuration
public class DynamicSchedulerConfig {
    @Bean
    public CustomDynamicSchedule getDynamicScheduler() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.initialize();
        return  new CustomDynamicSchedule(threadPoolTaskScheduler);
    }
}

和用法:

@EnableScheduling
@Component
public class TestSchedulerComponent {

    @Autowired
    private CustomDynamicSchedule dynamicSchedule;

    @Scheduled(fixedDelay = 5000)
    public void testMethod() {
        dynamicSchedule.delay(1000l);
        dynamicSchedule.increaseDelayInterval(9000l);
        dynamicSchedule.decreaseDelayInterval(5000l);
    }

}

您也可以为此使用 Spring 表达式语言 (SpEL)。

@Scheduled(fixedDelayString = "#{@applicationPropertyService.getApplicationProperty()}")
public void getSchedule(){
   System.out.println("in scheduled job");
}

@Service
public class ApplicationPropertyService {

    public String getApplicationProperty(){
        //get your value here
        return "5000";
    }
}

AFAIK Spring API 不会让您访问更改触发器所需的内部结构。 但是您可以改为手动配置 bean:

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
    <property name="jobDetail" ref="jobDetail" />
    <property name="startDelay" value="10000" />
    <property name="repeatInterval" value="50000" />
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="simpleTrigger" />
        </list>
    </property>
</bean>

然后如 SchedulerFactoryBean 中所述:

要在运行时动态注册作业,请使用对此 SchedulerFactoryBean 的 bean 引用来直接访问 Quartz 调度程序( org.quartz.Scheduler )。 这允许您创建新的作业和触发器,还可以控制和监视整个调度程序。

我处理过同样的问题。 我们需要在运行时更改 cron 表达式并重新安排服务。 所以应该有:

  • 无需重新编译
  • 没有重新部署
  • 没有重启

的应用程序。 我检查了所有流行的解决方案,但只有 2 个满足所有要求。

SchedulingConfigurer 方法的缺点是它是基于拉取的,即每次执行服务的业务逻辑时都会拉取调度配置。 这通常不是一件坏事,但是如果很少更改配置并且执行间隔很短,那么将会有很多不必要的请求。

自定义解决方案的缺点是它需要更多的编码,但它是基于推送的并对配置更改做出反应,因此不会执行不必​​要的请求/调用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM