how can you configure spring to execute overlapping fixedRate tasks?
i am trying to execute a task at a fixed rate using the @Scheduled annotation in java spring. however, it seems that by default spring will not execute a fixedRate task at a fixed rate if the task is slower than the rate. is there some setting i can add to my spring configuration to change this behavior?
example :
@Service
public class MyTask{
@Scheduled(fixedRate = 1000)
public void doIt(){
// this sometimes takes >1000ms, in which case the next execution is late
...
}
}
i have a work-around , but it seems less than ideal. basically, i just replace the default single-thread executor with a thread pool, then i have a scheduled method call an async method since the @Async annotation allows concurrent executions:
@Service
public class MyTask{
@Async
public void doIt(){
// this sometimes takes >1000ms, but the next execution is on time
...
}
}
@Service
public class MyTaskScheduler{
...
@Scheduled(fixedRate = 1000)
public void doIt(){
myTask.doIt();
}
}
@Configuration
@EnableScheduling
@EnableAsync
public class MySpringJavaConfig{
@Bean(destroyMethod = "shutdown")
public Executor taskScheduler() {
return Executors.newScheduledThreadPool(5);
}
}
boring details of my real-world scenario : in my production code i have a task that takes between 10ms and 10 minutes depending on the current workload. ideally, i would like to capture a new thread from the pool every 1000ms so that the number of concurrent threads increases with the workload. obviously i have an upper limit on threads in place (among other controls) to keep things from getting out of hand.
The TaskScheduler
API (which backs some of the Spring Scheduling behavior) seems to be defined to prevent the behavior you are requesting
Schedule the given Runnable
, invoking it at the specified execution time and subsequently with the given period.
Parameters
subsequently and successive seem to indicate that the next execution will only occur after the current execution is complete.
What's more, ScheduledExecutorService#scheduleAtFixedRate(..)
(which the built-in TaskScheduler
implementations use) also says
If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute .
So there's another layer of the implementation that prevents the behavior you want.
A possible solution, and one I don't recommend since the API doesn't seem to be built around it, is to define and provide your own TaskScheduler
that does run the task concurrently. Look into @EnableScheduling
and SchedulingConfigurer
for how to register a TaskScheduler
.
the best solution i have found so far is to simply use a delegate to make the method calls async. this is only preferable because it allows me to declare the schedule in the same class as the method which does the work:
@Service
public class AsyncRunner {
@Async
public void run(Runnable runnable) {
runnable.run();
}
}
@Service
public class MyTask{
...
@Scheduled(fixedRate = 1000)
public void scheduleIt(){
asyncRunner.run(this::doIt);
}
public void doIt(){
// this sometimes takes >1000ms, but the next execution is on time
...
}
}
链接地址: http://www.djcxy.com/p/85400.html
上一篇: 为什么编辑需要这么长时间?