Using Spring Task in Spring Boot to implement scheduled tasks

We often use scheduled tasks in daily project development. For example, obtaining information regularly, publishing tasks, etc. Today we will take a look at how to use Spring built-in in Spring Boot scheduled tasks.

1. Start scheduled tasks

Spring Boot uses the scheduled task tool Spring Task provided under the spring-context module by default without any third-party dependencies. We only need to use the @EnableScheduling annotation to enable the related scheduled task function. Such as:

 Then we can implement custom scheduled tasks through annotations.

2. @Scheduled annotation implements scheduled tasks

 You only need to define oneSpring Bean, then define the specific scheduled task logic method and use @Scheduled annotation Just mark the method.

@Scheduled The annotation must declare the execution strategy of the scheduled task: cron, fixedDelay, fixedRate.

3. Execution strategy

1. cron expression

This parameter receives a cron expression. The cron expression is a string. The string is separated by 5 or 6 spaces and divided into a total of 6 or 7 fields. Each field represents a meaning.

cron expression syntax:

Format: [second] [minute] [hour] [day] [month] [week] [year]

serial number illustrate   Is it required?     Allowed values ​​​​     Allowed wildcards
1 Second yes 0-59 , - * /
2 point yes 0-59 , - * /
3 hour yes 0-23 , - * /
4 Day yes 1-31 , - * ? / L W
5 moon yes 1-12 or JAN-DEC , - * /
6 week yes 1-7 or SUN-SAT , - * ? / L #
7 Year no empty or 1970-2099 , - * /

You can use the tool to generate Cron expressions online:Online Cron Expression Generator to generate the expression you want.


Wildcard description:

* means all values. For example: setting "*" on the minute field means it will trigger every minute.

? means no value is specified. The usage scenario is that you do not need to care about the current value of this field. For example: you want to trigger an operation on the 10th of every month, but you don’t care what day of the week it is, so you need to set the field of the week position to "?". The specific setting is 0 0 0 10 * ?

- represents an interval. For example, setting "10-12" on the hour means that it will be triggered at 10, 11, and 12 o'clock.

, means specifying multiple values, for example, setting "MON,WED,FRI" on the week field means triggering on Monday, Wednesday and Friday

/ is used for incremental triggering. For example, setting "5/15" above seconds means starting from 5 seconds and triggering every 15 seconds (5,20,35,50). Set '1/3' in the month field to start on the 1st of each month and trigger every three days.

L means last. In the day field setting, it represents the last day of the month (according to the current month, if it is February, it will also depend on whether it is a leap year), and in the week field it represents Saturday, which is equivalent to "7" or "SAT" . If a number is added before "L", it means the last one of the data. For example, setting a format like "6L" on the week field means "the last Friday of this month"

W represents the working day closest to the specified date (Monday to Friday). For example, setting "15W" on the day field means that the working day closest to the 15th of each month is triggered. If the 15th happens to be a Saturday, the trigger will be found on the nearest Friday (the 14th). If the 15th is a weekend, the trigger will be found on the nearest Monday (the 16th). If the 15th happens to be on a working day (Monday to Sunday) 5), it will be triggered on that day. If the specified format is "1W", it means that the trigger is triggered on the nearest working day after the 1st of each month. If the 1st falls on a Saturday, it will be triggered on Monday the 3rd. (Note, only specific numbers can be set before "W", and the range "-" is not allowed).

Tips: 'L' and 'W' can be used in combination. If "LW" is set on the day field, it means that it is triggered on the last working day of this month (generally refers to salary payment).

# Serial number (indicates the day of the week of each month), for example, setting "6#3" in the week field indicates the third Saturday of each month. Note that if "#5" is specified, it will be the fifth week If there is no Saturday, the configuration will not be triggered

2、fixedDelay

fixedDelay. Its interval is started based on the end of the last task. Just keep an eye on the end time of the last execution. It has nothing to do with the execution time of the task logic. The interval between the two rounds is fixed.

3、fixedRate

 fixedRate. This is relatively difficult to understand. Ideally, the time interval between the next start and the previous start would be constant. But by default, Spring Boot scheduled tasks are executed in a single thread.. When the next round of tasks meets the time policy, the task will be added to the queue. That is to say, when the current task starts to execute, the time of the next task has been determined. Due to the "timeout" execution of this task, the waiting time of the next task will be compressed or even blocked.

 4、initialDelay

initialDelay Initialization delay time, which is the time of the first delayed execution. This parameter is invalid for the cron attribute and can only be used with fixedDelay or fixedRate. For example, @Scheduled(initialDelay=5000,fixedDelay = 1000) means that the first delay is 5000 milliseconds to execute, and the next task will be executed 1000 milliseconds after the end of the previous task.

4.Disadvantages of Spring Task

If Spring Task does not understand some mechanisms in actual applications, there will be some problems, so the following points are very important.

1. Single-thread blocking execution

The scheduled tasks of Spring are executed in a single thread by default. In the case of multi-tasking, using multiple threads will affect the timing strategy. Let's demonstrate:

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * The type Task service.
 *
 * @author felord.cn
 * @since 11 :02
 */
@Component
public class TaskService {
    /**
     * 上一次任务结束后 1 秒,执行下一次任务,任务消耗 5秒
     *
     * @throws InterruptedException the interrupted exception
     */
    @Scheduled(fixedDelay = 1000)
    public void task() throws InterruptedException {
        System.out.println("Thread Name : "
                + Thread.currentThread().getName()
                + "  i am a task : date ->  "
                + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        Thread.sleep(5000);
    }
    /**
     * 下轮任务在本轮任务开始2秒后执行. 执行时间可忽略不计
     */
    @Scheduled(fixedRate = 2000)
    public void task2() {
        System.out.println("Thread Name : "
                + Thread.currentThread().getName()
                + "  i am a task2 : date ->  "
                + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }

}

 

 In other words, a "chain reaction" occurred due to single-thread blocking, resulting in confusion in task execution.

@EnableSchedulingThe annotation introduces ScheduledAnnotationBeanPostProcessor , whose setScheduler(Object scheduler) has the following annotation:

If TaskScheduler or ScheduledExecutorService are not defined as parameters for this method, the method will look for the only as a parameter. Of course, you can also find a by searching for . If you can't find it, you can only use the local single-thread scheduler. TaskSchedulertaskSchedulerTaskSchedulerScheduledExecutorService

The calling sequence relationship of Spring Task is:Task Scheduling Thread SchedulingTask execution thread executesscheduled task so we define one as aboveTaskScheduler Automatic configuration of is provided in Spring Boot automatic configuration: TaskScheduler

@ConditionalOnClass(ThreadPoolTaskScheduler.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TaskSchedulingProperties.class)
@AutoConfigureAfter(TaskExecutionAutoConfiguration.class)
public class TaskSchedulingAutoConfiguration {

	@Bean
	@ConditionalOnBean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
	@ConditionalOnMissingBean({ SchedulingConfigurer.class, TaskScheduler.class, ScheduledExecutorService.class })
	public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
		return builder.build();
	}

	@Bean
	@ConditionalOnMissingBean
	public TaskSchedulerBuilder taskSchedulerBuilder(TaskSchedulingProperties properties,
			ObjectProvider<TaskSchedulerCustomizer> taskSchedulerCustomizers) {
		TaskSchedulerBuilder builder = new TaskSchedulerBuilder();
		builder = builder.poolSize(properties.getPool().getSize());
		Shutdown shutdown = properties.getShutdown();
		builder = builder.awaitTermination(shutdown.isAwaitTermination());
		builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
		builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
		builder = builder.customizers(taskSchedulerCustomizers);
		return builder;
	}

}

The custom configuration for this configuration begins with spring.task.scheduling. At the same time, it needs to be configured after the task executor configuration TaskExecutionAutoConfiguration is configured. We only need to configure its properties in spring.task.execution related properties.

application.propertiesRelevant configuration instructions in of :Spring Boot

# 任务调度线程池

# 任务调度线程池大小 默认 1 建议根据任务加大
spring.task.scheduling.pool.size=1
# 调度线程名称前缀 默认 scheduling-
spring.task.scheduling.thread-name-prefix=scheduling-
# 线程池关闭时等待所有任务完成
spring.task.scheduling.shutdown.await-termination=
# 调度线程关闭前最大等待时间,确保最后一定关闭
spring.task.scheduling.shutdown.await-termination-period=


# 任务执行线程池配置

# 是否允许核心线程超时。这样可以动态增加和缩小线程池
spring.task.execution.pool.allow-core-thread-timeout=true
#  核心线程池大小 默认 8
spring.task.execution.pool.core-size=8
# 线程空闲等待时间 默认 60s
spring.task.execution.pool.keep-alive=60s
# 线程池最大数  根据任务定制
spring.task.execution.pool.max-size=
#  线程池 队列容量大小
spring.task.execution.pool.queue-capacity=
# 线程池关闭时等待所有任务完成
spring.task.execution.shutdown.await-termination=true
# 执行线程关闭前最大等待时间,确保最后一定关闭
spring.task.execution.shutdown.await-termination-period=
# 线程名称前缀
spring.task.execution.thread-name-prefix=task-

After configuring, you will find that scheduled tasks can be executed in parallel and asynchronously.

2, Distributed is not supported by default

Spring Task is not designed for distributed environments. In distributed environments, this kind of scheduled tasks does not support cluster configuration. If deployed on multiple nodes, there is no coordination communication mechanism between each node. The nodes of the cluster Task information will not be shared between nodes, and tasks on each node will be executed on time, resulting in repeated execution of tasks. We can use scheduled task scheduling frameworks that support distributed distribution, such as Quartz, XXL-Job, and Elastic Job. Of course, you can use zookeeper, redis, etc. to implement distributed locks to handle the coordination issues of each node. Or separate all scheduled tasks into separate services and deploy them separately.

Guess you like

Origin blog.csdn.net/cyl101816/article/details/126383138