有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java根据springboot@Scheduled注释使用的条件动态修改调度程序计时

我有一个调度程序,它以5秒的固定延迟触发
我计划有多个调度器,但现在,让我们只使用一个调度器

要求:应根据业务情况更改计划程序的固定延迟
**例如,**默认固定延迟5秒,但根据条件,它可以是6、8、10秒

因此,为了实现这一点,我试图修改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);
}


实现位于组织的触发器接口。springframework。spring上下文项目中的调度。

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上运行,这方面没有变化

请帮忙


共 (3) 个答案

  1. # 1 楼答案

    有了注释,你只能通过找到一个公分母并进行投票来进行近似运算。我一会儿给你看。如果您想要一个真正的动态解决方案,您不能使用注释,但可以使用编程配置此解决方案的优点是,您甚至可以在运行时更改执行周期下面是一个如何做到这一点的示例:

      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
       }
    }
    
    }
    

    我有点跛脚,但我会为简单的案件做这项工作

  2. # 2 楼答案

    Spring的@Scheduled注释不提供这种支持。

    这是我首选的使用基于队列的解决方案实现类似功能的方法,该解决方案允许灵活的计时和非常健壮的调度器功能实现。

    这是管道- enter image description here

    1. Cron维护者和发布者-对于每个任务,都有一个附属的cron和一个单线程执行器服务,负责根据Cron将消息发布到队列。task-cron映射被持久化在database中,并在启动期间初始化。此外,我们还公开了一个API,用于在运行时更新任务的cron。

      我们只需关闭旧的scheduled executor服务,并在通过API触发cron更改时创建一个服务。此外,我们还更新了数据库中的数据

    2. 队列-用于存储发布者发布的消息
    3. 调度程序-这是调度程序的业务逻辑所在。队列上的侦听器(在我们的例子中是Kafka)侦听传入的消息,并在新线程中调用相应的调度程序任务,只要它收到相同的消息。

    这种方法有很多优点。这将使调度器与计划管理任务分离。现在,调度器可以只关注业务逻辑。此外,我们可以编写任意多个调度器,所有调度器都监听同一个队列,并相应地执行操作

  3. # 3 楼答案

    使用@Scheduled只允许使用静态计划。您可以使用属性使计划可以这样配置

    @Scheduled(cron = "${yourConfiguration.cronExpression}")
    
    // or
    
    @Scheduled(fixedDelayString = "${yourConfiguration.fixedDelay}")
    

    但是,一旦spring上下文初始化(应用程序启动),生成的时间表将被修复

    要获得对计划执行的细粒度控制,您需要实现一个定制的^{}——类似于您已经做过的。与待执行任务一起,可以通过使用^{}@Configuration类中实现^{}来注册此触发器:

    @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中创建为单线程,并且可能被多个线程并行访问