How to change Spring's @Scheduled fixedDelay at runtime

前端 未结 5 1867
予麋鹿
予麋鹿 2020-11-30 03:00

I have a requirement to run a batch job at a fixed interval and have the ability to change the time of this batch job at runtime. For this I came across @Scheduled annotatio

相关标签:
5条回答
  • 2020-11-30 03:33

    AFAIK the Spring API won't let you access the internals you need to change the trigger. But you could instead configure manually the beans:

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

    Then as documented in SchedulerFactoryBean:

    For dynamic registration of jobs at runtime, use a bean reference to this SchedulerFactoryBean to get direct access to the Quartz Scheduler (org.quartz.Scheduler). This allows you to create new jobs and triggers, and also to control and monitor the entire Scheduler.

    0 讨论(0)
  • 2020-11-30 03:43

    In spring boot, you can use an application property directly!

    For example:

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

    Note that you can also have a default value in case the property isn't defined, eg to have a default of "60" (seconds):

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

    Other things I discovered:

    • the method must be void
    • the method must have no parameters
    • the method may be private

    I found being able to use private visibility handy and used it in this way:

    @Service
    public class MyService {
        public void process() {
            // do something
        }
    
        @Scheduled(fixedDelayString = "${my.poll.fixed.delay.seconds}000")
        private void autoProcess() {
            process();
        }
    }
    

    Being private, the scheduled method can be local to your service and not become part of your Service's API.

    Also, this approach allows the process() method to return a value, which a @Scheduled method may not. For example, your process() method can look like:

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

    to provide some information about what happened during processing.

    0 讨论(0)
  • 2020-11-30 03:50

    You can also use Spring Expression Language (SpEL) for this.

    @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";
        }
    }
    
    0 讨论(0)
  • 2020-11-30 03:57

    You can use a Trigger to dynamically set the next execution time. See my answer here:

    Scheduling a job with Spring programmatically (with fixedRate set dynamically)

    0 讨论(0)
  • 2020-11-30 03:58

    create interface , something like that:

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

    Next, lets implement Trigger interface that is located at org.springframework.scheduling in the spring-context project.

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

    now configuration:

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

    and usage:

    @EnableScheduling
    @Component
    public class TestSchedulerComponent {
    
        @Autowired
        private CustomDynamicSchedule dynamicSchedule;
    
        @Scheduled(fixedDelay = 5000)
        public void testMethod() {
            dynamicSchedule.delay(1000l);
            dynamicSchedule.increaseDelayInterval(9000l);
            dynamicSchedule.decreaseDelayInterval(5000l);
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题