SpirngBoot uses @ Scheduled to start multiple Scheduled tasks and occasionally fails to execute

Posted by raj86 on Wed, 15 Dec 2021 00:58:57 +0100

In the project, it is necessary to configure Scheduled tasks to collect hdfs-jmx data for reading and writing database operations, and configure some Scheduled tasks for alarm operations. The @ Scheduled annotation is used.
When 7 scheduled tasks are configured every 5s according to the end time, a problem is found that some scheduled tasks are not executed on time, but they are not executed every time, nor are they executed by the same thread every time. It's strange. I went to see what's going on

Some scheduled task codes configured in the project are as follows:

    @Scheduled(fixedDelay = 5000)
    public void updateTotalDifference() throws IOException {
        summaryController.updateTotalDifference();
    }
    @Scheduled(fixedDelay = 5000)
    public void update1() throws IOException {
        dataNodeController.updateDNGroupFirst();
    }
    @Scheduled(fixedDelay = 5000)
    public void update2() throws IOException {
        dataNodeController.updateDNGroupSecond();
    }
    @Scheduled(fixedDelay = 5000)
    public void update3() throws IOException {
        summaryController.updateNNActiveAndStandby();
    }

I found that the program occasionally updates DN but not NN, occasionally updates NN but not DN, and the alarm timing task executed every 30s and every 1h will be executed every time. In order to find the reason, you need to look at the @ Scheduled source code

 *
 * <p>Processing of {@code @Scheduled} annotations is performed by
 * registering a {@link ScheduledAnnotationBeanPostProcessor}. This can be
 * done manually or, more conveniently, through the {@code <task:annotation-driven/>}
 * element or @{@link EnableScheduling} annotation.
 *

Translation:
{@ code@scheduledAnotationBeanPostProcessor }The processing of is performed by registering {@ link scheduledanotionbeanpostprocessor}. This may be done manually, or more conveniently through {@ code < task: annotation driven / >} elements or @ {@ link enableshcheduling} annotations.

Through this comment, we can find that every method marked by @ Scheduled annotation will be registered as a ScheduledAnnotationBeanPostProcessor. Then, we will look at what is ScheduledAnnotationBeanPostProcessor:

/**
	 * Set the {@link org.springframework.scheduling.TaskScheduler} that will invoke
	 * the scheduled methods, or a {@link java.util.concurrent.ScheduledExecutorService}
	 * to be wrapped as a TaskScheduler.
	 * <p>If not specified, default scheduler resolution will apply: searching for a
	 * unique {@link TaskScheduler} bean in the context, or for a {@link TaskScheduler}
	 * bean named "taskScheduler" otherwise; the same lookup will also be performed for
	 * a {@link ScheduledExecutorService} bean. If neither of the two is resolvable,
	 * a local single-threaded default scheduler will be created within the registrar.
	 * @see #DEFAULT_TASK_SCHEDULER_BEAN_NAME
	 */
	public void setScheduler(Object scheduler) {
		this.scheduler = scheduler;
	}

Translated as:

Set {@ link org.springframework.scheduling.TaskScheduler} that will call the scheduling method, or wrap {@ link java.util.concurrent.ScheduledExecutorService} as a TaskScheduler. If not specified, the default scheduler solution will be applied: search the unique {@ link TaskScheduler}bean in the context, otherwise search the {@ link TaskScheduler}bean named "TaskScheduler"; The same lookup will be performed on the {@ link ScheduledExecutorService}bean. If neither is resolvable, a local single threaded default scheduler is created in the Registrar@ See # default (task) scheduler (BEAN) name

We can find a sentence here: if the TaskScheduler is not specified, the default scheduler solution will be applied: search the unique {@ link TaskScheduler}bean in the context, otherwise search the {@ link TaskScheduler}bean named "TaskScheduler"; The same lookup will be performed on the {@ link ScheduledExecutorService}bean. If neither is resolvable, a local single threaded default scheduler is created in the Registrar.

That is to say, if we do not actively configure the task Scheduler, SpirngBoot will use a single threaded Scheduler by default to process all the scheduled tasks we implement with @ Scheduler annotation. It can be understood that of my seven scheduled tasks, four are at the same time and three are at different times, that is, the four scheduled tasks are executed by single thread, Therefore, a task is not executed occasionally, but not every time

Then we need to create a TaskScheduler and register it in the context:

/**
 * @author Zenglin.Fang
 * @ClassName ScheduledTaskConfiguration
 * @Description:
 * @Data: 2021/9/4 15:25
 **/
@Configuration
public class ScheduledTaskConfiguration implements SchedulingConfigurer {
    /**
     * Callback allowing a {@link TaskScheduler
     * TaskScheduler} and specific {@link Task Task}
     * instances to be registered against the given the {@link ScheduledTaskRegistrar}
     *
     * @param taskRegistrar the registrar to be configured.
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        final ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.initialize();
        taskRegistrar.setTaskScheduler(taskScheduler);
    }
}

Here, I created a tashScheduler with a thread pool size of 10. After uploading git and publishing the new version, I found that the problem was solved. The execution of each scheduled task did not affect each other, and all DN and NN could be updated normally.

Finally, attach the @ Scheduled annotation parameter:

Parameter valueeffect
croncron expression setting timing
zoneTake the time zone where the server is located, and this field is generally left blank
fixedDelayAfter the last task is completed, execute the next task at an interval of (x/1000) seconds
fixedDelayStringConsistent with the meaning of fixedDelay, it uses string form and supports placeholders
fixedRateThe task is executed once every (x/1000) seconds. Whether the last task is completed or not, it can be used in conjunction with initiaDelay
fixedRateStringConsistent with the meaning of fixedRate, it uses string form and supports placeholders
initiaDelayWait (x/1000) seconds before executing the task for the first time and delay (x/1000) seconds
initialDelayStringConsistent with the meaning of initialDelay, it uses the form of string and supports placeholders

Topics: Java Spring Boot