[英]Execute two schedulers simultaneously with spring-boot @scheduled annotation
[英]Modify scheduler timing dynamically based on the condition used with spring-boot @Scheduled annotation
我有一个调度程序,它以 5 秒的固定延迟触发。
我计划拥有多个调度程序,但现在,让我们只使用一个调度程序。
需求:根据业务条件调度器的fixedDelay应该改变。
**例如,**默认的fixedDelay是5secs ,但根据条件可以是6, 8, 10secs 。
因此,为了实现这一点,我正在尝试修改fixedDelay 。 但这对我不起作用。
代码:
接口,带有延迟方法。
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 getDinamicScheduler() {
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);
}
}
我得到了https://stackoverflow.com/a/51333059/4770397 的帮助,
但不幸的是,这段代码对我不起作用。
调度程序在fixedDelay运行,没有变化。
请帮忙..
使用@Scheduled
将只允许使用静态计划。 您可以使用属性使计划可配置,如下所示
@Scheduled(cron = "${yourConfiguration.cronExpression}")
// or
@Scheduled(fixedDelayString = "${yourConfiguration.fixedDelay}")
但是,一旦您的 spring 上下文初始化(应用程序启动),结果计划将被修复。
要对预定执行进行细粒度控制,您需要实现一个自定义Trigger
- 类似于您已经做过的。 与要执行的任务一起,可以通过在@Configuration
类中使用ScheduledTaskRegistrar.addTriggerTask
实现SchedulingConfigurer
来注册此触发器:
@Configuration
@EnableScheduling
public class AppConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler());
taskRegistrar.addTriggerTask(() -> myTask().work(), myTrigger());
}
@Bean(destroyMethod="shutdown")
public Executor taskScheduler() {
return Executors.newScheduledThreadPool(42);
}
@Bean
public CustomDynamicSchedule myTrigger() {
new CustomDynamicSchedule();
}
@Bean
public MyTask myTask() {
return new MyTask();
}
}
但是不要在CustomDynamicSchedule
注册任何任务,只需使用它来计算下一次执行时间:
public class CustomDynamicSchedule extends DynamicSchedule implements Trigger {
private long delayInterval;
@Override
public synchronized void increaseDelayInterval(Long delay) {
this.delayInterval += delay;
}
@Override
public synchronized void decreaseDelayInterval(Long delay) {
this.delayInterval += delay;
}
@Override
public synchronized void delay(Long delay) {
this.delayInterval = delay;
}
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Date lastTime = triggerContext.lastActualExecutionTime();
return (lastTime == null) ? new Date() : new Date(lastTime.getTime() + delayInterval);
}
}
但请记住使CustomDynamicSchedule
线程安全,因为它会被 spring 创建为单例,并且可能被多个线程并行访问。
Spring 的@Scheduled
注释不提供这种支持。
这是我使用基于队列的解决方案的类似功能的首选实现,它允许时间的灵活性和此调度程序功能的非常健壮的实现。
cron
和一个单线程执行器服务,负责根据 cron 将消息发布到队列。 task-cron
映射持久保存在database
,并在启动期间进行初始化。 此外,我们公开了一个API
,用于在运行时更新任务的 cron。我们只需关闭旧的计划执行器服务,并在我们通过 API 触发 cron 更改时创建一个。 此外,我们在数据库中更新相同。
这种方法有多种优点。 这将调度程序与调度管理任务分离。 现在,调度程序可以单独关注业务逻辑。 此外,我们可以根据需要编写任意数量的调度程序,所有调度程序都侦听相同的队列并相应地执行操作。
使用注释,您只能通过找到一个公分母并对其进行轮询来进行近似。 我稍后会告诉你。 如果你想要一个真正的动态解决方案,你不能使用注解,但你可以使用编程配置。 该解决方案的优点是您甚至可以在运行时更改执行周期! 这是一个关于如何做到这一点的例子:
public initializeDynamicScheduledTAsk (ThreadPoolTaskScheduler scheduler,Date start,long executionPeriod) {
scheduler.schedule(
new ScheduledTask(),
new Date(startTime),period
);
}
class ScheduledTask implements Runnable{
@Override
public void run() {
// my scheduled logic here
}
}
有一种方法可以欺骗并实际使用注释做一些事情。 但只有在精度不重要的情况下才能这样做。 精度是什么意思。 如果您知道要每 5 秒启动一次,但 100 毫秒或多或少并不重要。 如果您知道需要每 5-6-8 秒或 10 秒启动一次,您可以配置一个每秒执行一次的作业,并在一个 if 语句中检查自上次执行以来已经过去了多长时间。 它非常蹩脚,但它可以工作:) 只要您不需要高达毫秒的精度。 下面是一个例子:
public class SemiDynamicScheduledService {
private Long lastExecution;
@Value(#{yourDynamicConfiguration})
private int executeEveryInMS
@Scheduled(fixedDelay=1000)
public semiDynamicScheduledMethod() {
if (System.currentTimeMS() - lastExecution>executeEveryInMS) {
lastExecution = System.currentTimeMS();
// put your processing logic here
}
}
}
我有点蹩脚,但会在简单的情况下完成这项工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.