Revisit the asynchronous and timed tasks of the SpringBoot series
- Implementing Async asynchronous tasks
- Planning thread pools for asynchronous tasks
- Implement Scheduled tasks through @ Scheduled
- quartz simple timing task (memory persistence)
- quartz dynamic timing task (database persistence)
- Distributed task scheduling framework -- XXL job
Implementing Async asynchronous tasks
Environmental preparation

Configure the @ EnableAsync annotation on the Spring Boot entry class to enable asynchronous processing. Create a task abstract class AbstractTask, and configure three task methods dotaskane(), doTaskTwo(), doTaskThree().
public abstract class AbstractTask { private static Random random = new Random(); public void doTaskOne() throws Exception { System.out.println("Start task one"); long start = currentTimeMillis(); sleep(random.nextInt(10000)); long end = currentTimeMillis(); System.out.println("Completing task 1 takes:" + (end - start) + "millisecond"); } public void doTaskTwo() throws Exception { System.out.println("Start task two"); long start = currentTimeMillis(); sleep(random.nextInt(10000)); long end = currentTimeMillis(); System.out.println("Completing task 2 takes:" + (end - start) + "millisecond"); } public void doTaskThree() throws Exception { System.out.println("Start task three"); long start = currentTimeMillis(); sleep(random.nextInt(10000)); long end = currentTimeMillis(); System.out.println("Completing task 3 takes:" + (end - start) + "millisecond"); } }
Synchronous call
Here is a simple example to intuitively understand what synchronous call is:
- Define the Task class and inherit AbstractTask. The three processing functions simulate three operations to execute tasks respectively. The operation consumption time is taken randomly (10 seconds).
@Component public class SyncTask extends AbstractTask { }
- In the unit test case, inject SyncTask object, and execute dotaskane(), doTaskTwo(), doTaskThree() in the test case.
@RunWith(SpringRunner.class) @SpringBootTest public class TaskTest { @Autowired private SyncTask task; @Test public void testSyncTasks() throws Exception { task.doTaskOne(); task.doTaskTwo(); task.doTaskThree(); } }
After executing the unit test, you can see the following outputs:
Start task one Completion of task 1 takes 6720 milliseconds Start task two Completion of task 2 takes 6604 milliseconds Start task three Completing task 3 takes 9448 milliseconds

Task 1, task 2 and task 3 are executed in sequence. In other words, dotaskane(), doTaskTwo(), doTaskThree() are executed in sequence.
Asynchronous call
Although the above synchronous calls have successfully executed the three tasks, it can be seen that the execution time is relatively long. If there is no dependency between the three tasks and they can be executed concurrently, the execution efficiency of synchronous calls is relatively poor. You can consider concurrent execution through asynchronous calls.
- Add @ EnableAsync to the Application startup class
- Create the AsyncTask class, configure the @ Async annotation on the method respectively, and change the original synchronous method into asynchronous method.
@Component public class SyncTask extends AbstractTask { @Async @Override public void doTaskOne() throws Exception { super.doTaskOne(); } @Async @Override public void doTaskTwo() throws Exception { super.doTaskTwo(); } @Async @Override public void doTaskThree() throws Exception { super.doTaskThree(); } }
- In the unit test case, inject the AsyncTask object, and execute dotaskane(), doTaskTwo(), doTaskThree() in the test case.
@Autowired private AsyncTask asyncTask; @Test public void testAsyncTasks() throws Exception { asyncTask.doTaskOne(); asyncTask.doTaskTwo(); asyncTask.doTaskThree(); }
- After executing the unit test, you can see the following outputs:
Start task three Start task one Start task two
If you perform unit tests repeatedly, you may encounter different results, such as:
- There are no task related outputs
- There are some task related outputs
- Out of order task related output

The reason is that the three methods dotaskane(), doTaskTwo(), dotasktree() have been executed asynchronously and concurrently. After the main program is called asynchronously, the main program does not care whether the execution of these three functions is completed. Because there is no other content to be executed, the program ends automatically, resulting in incomplete tasks or no output of relevant content.
Note: the function modified by @ Async should not be defined as static type, so the asynchronous call will not take effect
Asynchronous callback
In order for dotaskane(), doTaskTwo(), doTaskThree() to end normally, suppose we need to count the total time taken for the concurrent execution of the three tasks, so we need to wait until the above three functions are used, record the time and calculate the results.
So how do we judge whether the above three asynchronous calls have been executed? We need to use future < T > to return the results of asynchronous calls.
- Create AsyncCallBackTask class, declare dotaskanecallback(), doTaskTwoCallback(), doTaskThreeCallback(), and wrap the original three methods.
@Component public class AsyncCallBackTask extends AbstractTask { @Async public Future<String> doTaskOneCallback() throws Exception { super.doTaskOne(); return new AsyncResult<>("Once the task is completed"); } @Async public Future<String> doTaskTwoCallback() throws Exception { super.doTaskTwo(); return new AsyncResult<>("Task 2 completed"); } @Async public Future<String> doTaskThreeCallback() throws Exception { super.doTaskThree(); return new AsyncResult<>("Task 3 completed"); } }
- In the unit test case, inject the AsyncCallBackTask object, and execute the three methods dotaskanecallback(), doTaskTwoCallback(), doTaskThreeCallback() in the test case. Call the isDone() method of Future circularly, wait for the execution of three concurrent tasks to complete, and record the final execution time.
@Autowired private AsyncCallBackTask asyncCallBackTask; @Test public void testAsyncCallbackTask() throws Exception { long start = currentTimeMillis(); Future<String> task1 = asyncCallBackTask.doTaskOneCallback(); Future<String> task2 = asyncCallBackTask.doTaskTwoCallback(); Future<String> task3 = asyncCallBackTask.doTaskThreeCallback(); // After all three tasks are called, exit the loop and wait while (!task1.isDone() || !task2.isDone() || !task3.isDone()) { sleep(1000); } long end = currentTimeMillis(); System.out.println("Complete all tasks, total time:" + (end - start) + "millisecond"); }
See what changes have been made:
- Record the start time at the beginning of the test case;
- When calling three asynchronous functions, return the result object of type Future;
- After calling the three asynchronous functions, open a loop to judge whether the three asynchronous functions have ended according to the returned Future object. If all ends, the cycle ends; If not, wait 1 second before judging.
- After jumping out of the loop, calculate the total time spent on the concurrent execution of the three tasks according to the end time start time.

Perform the above unit test, and you can see the following results:
Start task three Start task one Start task two Completing task 2 takes 2572 milliseconds Completing task 1 takes 7333 milliseconds Completion of task 3 takes 7647 milliseconds Complete all tasks, total time: 8013 milliseconds
It can be seen that through asynchronous call, task 1, task 2 and task 3 can be executed concurrently, which effectively reduces the total running time of the program.
Planning thread pools for asynchronous tasks
Spring Boot task thread pool
Role of thread pool
- Prevent unlimited expansion of resource occupation
- Calling the procedure saves the time of resource creation and destruction
In the previous section, one of our asynchronous tasks opened a thread and destroyed it after completion. In the high concurrency environment, the continuous allocation of new resources may lead to the depletion of system resources. So to avoid this problem, we plan a thread pool for asynchronous tasks. Of course, if the thread pool is not configured, spring boot will automatically configure a ThreadPoolTaskExecutor thread pool into the bean.
# Number of core threads spring.task.execution.pool.core-size=8 # Maximum number of threads spring.task.execution.pool.max-size=16 # Idle thread lifetime spring.task.execution.pool.keep-alive=60s # Allow core threads to time out spring.task.execution.pool.allow-core-thread-timeout=true # Number of thread queues spring.task.execution.pool.queue-capacity=100 # Thread shutdown wait spring.task.execution.shutdown.await-termination=false spring.task.execution.shutdown.await-termination-period= # Thread name prefix spring.task.execution.thread-name-prefix=task-
Custom thread pool
Sometimes, we want to put one type of tasks in the system into one thread pool and another type of tasks into another thread pool, so we are stretched to use the task thread pool provided by Spring Boot. The following describes how to customize the thread pool.
Create a thread pool configuration class TaskConfiguration, and configure a task thread pool object taskExecutor.
@Configuration public class TaskConfiguration { @Bean("taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.setQueueCapacity(200); executor.setKeepAliveSeconds(60); executor.setThreadNamePrefix("taskExecutor-"); executor.setRejectedExecutionHandler(new CallerRunsPolicy()); return executor; } }

Above, we created a thread pool by using ThreadPoolTaskExecutor, and set the following parameters:

There are four predefined Reject policies:
- AbortPolicy, the handler for the rejected task, which will throw RejectedExecutionException.
- CallerRunsPolicy, the handler for the rejected task, which runs the rejected task directly in the calling thread of the execute method.
- DiscardOldestPolicy, a handler for rejected tasks, which discards the oldest unprocessed request and then retries execute.
- DiscardPolicy, which is used to handle rejected tasks. By default, it will discard rejected tasks.
Create the AsyncExecutorTask class. The configuration of the three tasks is the same as that of AsyncTask. The difference is that the @ Async annotation needs to specify the name of the previously configured thread pool taskExecutor.
@Component public class AsyncExecutorTask extends AbstractTask { @Async("taskExecutor") public Future<String> doTaskOneCallback() throws Exception { super.doTaskOne(); System.out.println("Task 1: current thread:" + Thread.currentThread().getName()); return new AsyncResult<>("Once the task is completed"); } @Async("taskExecutor") public Future<String> doTaskTwoCallback() throws Exception { super.doTaskTwo(); System.out.println("Task 2: current thread:" + Thread.currentThread().getName()); return new AsyncResult<>("Task 2 completed"); } @Async("taskExecutor") public Future<String> doTaskThreeCallback() throws Exception { super.doTaskThree(); System.out.println("Task 3: current thread:" + Thread.currentThread().getName()); return new AsyncResult<>("Task 3 completed"); } }
In the unit test case, inject the AsyncExecutorTask object, and execute dotaskane(), doTaskTwo(), doTaskThree() in the test case.
@SpringBootTest public class AsyncExecutorTaskTest { @Autowired private AsyncExecutorTask task; @Test public void testAsyncExecutorTask() throws Exception { task.doTaskOneCallback(); task.doTaskTwoCallback(); task.doTaskThreeCallback(); sleep(30 * 1000L); } }
Perform the above unit test, and you can see the following results:
Start task one Start task three Start task two Completion of task 2: 3905 MS Task 2: current thread: taskExecutor-2 Completing task 1 takes 6184 milliseconds Task 1: current thread: taskExecutor-1 Complete task 3, time consuming: 9737 milliseconds Task 3: current thread: taskExecutor-3
Execute the above unit test and observe that the prefix of the thread pool name of the task thread pool is printed, indicating that the thread pool successfully executes asynchronous tasks!
Gracefully close the thread pool
Because the asynchronous task is still executing when the application is closed, objects such as database connection pool are destroyed. When the asynchronous task operates on the database, an error will occur.
The solution is as follows: reset the thread pool configuration object and add the thread pool setwaitfortastocompleteonshutdown() and setAwaitTerminationSeconds() configurations:
@Bean("taskExecutor") public Executor taskExecutor() { ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler(); executor.setPoolSize(20); executor.setThreadNamePrefix("taskExecutor-"); executor.setWaitForTasksToCompleteOnShutdown(true); executor.setAwaitTerminationSeconds(60); return executor; }
- Setwaitfortastocompleteonshutdown (true): this method is used to set the wait time when the thread pool is closed After all tasks are completed, continue to destroy other beans, so that the destruction of these asynchronous tasks will precede the destruction of database connection pool objects.
- setAwaitTerminationSeconds(60): this method is used to set the number of threads in the thread pool The waiting time of the task. If the task is not destroyed after this time, it will be forcibly destroyed to ensure that the application can be closed rather than blocked.
Implement Scheduled tasks through @ Scheduled
Start scheduled task method
Scheduled scheduled task is a function provided by Spring boot itself, so Maven dependency package does not need to be introduced
Annotate the main method of the project entry
@EnableScheduling //Start scheduled task
Analysis of different timing modes
1.fixedDelay and fixedRate are in milliseconds. The difference between them is:

- fixedRate is how often it is executed. (start -------- > X time -------- start again). If the interval is less than the task execution time, the last task execution is completed and the next task is executed immediately. If the interval is greater than the task execution time, it will be run every X time.
- fixedDelay is to execute the task again after a period of time. (start - > end (one minute apart) start - > end). If the last task is not completed, the next task will not start
cron expressions: flexible
Examples

- The first digit indicates seconds, and the value is 0-59
- The second digit indicates score, and the value is 0-59
- The third digit indicates the hour, and the value is 0-23
- Fourth digit, date / day, value 1-31
- The fifth digit is the date month, and the value is 1-12
- Sixth, week, values 1-7, Monday, Tuesday... Note: not the first week, the second week means, in addition: 1 means Sunday, 2 means Monday.
- The seventh digit, year, can be left blank, and the value is 1970-2099
There are also some special symbols in cron, which have the following meanings:
- (*) asterisk: can be understood as the meaning of every, every second, every minute, every day, every month, every year
- (?) question mark: question mark can only appear in date and week.
- (-) minus sign: indicates a range. If "10-12" is used in the hour field, it means from 10 to 12 points, that is, 10,11,12
- (,) Comma: indicates a list value. If "1,2,4" is used in the week field, it means Monday, Tuesday and Thursday
- (/) slash: for example, x/y, x is the starting value, y is the step size, for example, in the first bit (second), 0 / 15 is, starting from 0 seconds, every 15 seconds, and finally 0, 15, 30, 45, 60, and: / y, which is equivalent to 0/y
cron expression online: http://cron.qqe2.com/
Implement scheduled tasks
@Component public class ScheduledJobs { //Indicates that the method will not be executed until 5 seconds after execution @Scheduled(fixedDelay=5000) public void fixedDelayJob() throws InterruptedException{ System.out.println("fixedDelay start:" + new Date()); Thread.sleep(10 * 1000); System.out.println("fixedDelay end:" + new Date()); } //Indicates every 3 seconds @Scheduled(fixedRate=3000) public void fixedRateJob()throws InterruptedException{ System.out.println("===========fixedRate start:" + new Date()); Thread.sleep(5 * 1000); System.out.println("===========fixedRate end:" + new Date()); } //Indicates that it is executed every 10 seconds @Scheduled(cron="0/10 * * * * ? ") public void cronJob(){ System.out.println("=========================== ...>>cron...." + new Date()); } }
The operation results are as follows: from the operation results, it does not operate according to the expected time law. Looking at thread printing carefully, it turns out that all scheduled tasks use one thread, so they affect each other.
===========fixedRate end:Tue Jul 09 19:53:04 CST 2019pool-1-thread-1 fixedDelay start:Tue Jul 09 19:53:04 CST 2019pool-1-thread-1 fixedDelay end:Tue Jul 09 19:53:14 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:14 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:16 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:16 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:18 CST 2019pool-1-thread-1 =========================== ...>>cron....Tue Jul 09 19:53:18 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:18 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:20 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:20 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:22 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:22 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:24 CST 2019pool-1-thread-1 fixedDelay start:Tue Jul 09 19:53:24 CST 2019pool-1-thread-1 fixedDelay end:Tue Jul 09 19:53:34 CST 2019pool-1-thread-1 =========================== ...>>cron....Tue Jul 09 19:53:34 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:34 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:36 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:36 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:38 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:38 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:40 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:40 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:42 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:42 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:44 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:44 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:46 CST 2019pool-1-thread-1 ===========fixedRate start:Tue Jul 09 19:53:46 CST 2019pool-1-thread-1 ===========fixedRate end:Tue Jul 09 19:53:48 CST 2019pool-1-thread-1 fixedDelay start:Tue Jul 09 19:53:48 CST 2019pool-1-thread-1 fixedDelay end:Tue Jul 09 19:53:58 CST 2019pool-1-thread-1 =========================== ...>>cron....Tue Jul 09 19:53:58 CST 2019pool-1-thread-1
Solve the problem of single thread running of scheduled tasks
@Configuration @EnableScheduling public class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(scheduledTaskExecutor()); } @Bean public Executor scheduledTaskExecutor() { return Executors.newScheduledThreadPool(3); //Specifies the thread pool size } }
Run the above program again, and the running time law will meet the expectations.
quartz simple timing task (memory persistence)
Quartz is another open source project of OpenSymphony open source organization in the field of work plan - scheduled tasks. It is completely developed by Java and can be used to perform predetermined tasks. It is similar to the java.util.Timer timer. However, compared with timer, quartz adds many functions.
Introduce the corresponding maven dependency
After spring boot 2.0, the dependency of Quartz framework is officially added, so it only needs to be introduced in the pom file
<!--introduce quartz Timing frame--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
Create a task class Job
First, we need to define an interface to implement the timing function. We can call it task (or task), for example, the task of sending email regularly, the task of restarting the machine, and the task of sending SMS reminder when the coupon expires.

Because springboot 2.0 automatically relies on it, the created scheduled task class can directly inherit QuzrtzJobBean and create a new scheduled task class: QuartzSimpleTask
public class QuartzSimpleTask extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println("quartz Simple scheduled task execution time:"+new Date().toLocaleString()); } }
Create Quartz timing configuration class
You also need a trigger that can trigger task execution. The basic function of trigger is to specify the execution time, execution interval and running time of the job.

How to combine work and trigger? That is, how are triggers assigned to execute the specified job? At this point, you need a Schedule to implement this function.

Add the previously created scheduled task to the scheduled schedule
@Configuration public class QuartzSimpleConfig { //Specify a specific scheduled task class @Bean public JobDetail uploadTaskDetail() { return JobBuilder.newJob(QuartzSimpleTask.class) .withIdentity("QuartzSimpleTask") .storeDurably().build(); } @Bean public Trigger uploadTaskTrigger() { //The trigger execution mode is set here CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("*/5 * * * * ?"); // Return task trigger return TriggerBuilder.newTrigger().forJob(uploadTaskDetail()) .withIdentity("QuartzSimpleTask") .withSchedule(scheduleBuilder) .build(); } }
Finally, run the project to check the effect. "* / 5 * *?" indicates a scheduled task, which is executed every 5 seconds.

As soon as the project starts, the scheduled task will begin to execute
In depth analysis
Core concept
- Job: a simple interface that contains only one void execute (JobExecutionContext) abstract method. In the actual development, the tasks to be performed are realized through the implementation of interface customization. JobExecutionContext provides scheduling context information.
public interface Job { void execute(JobExecutionContext context) throws JobExecutionException; }
- JobDetail: contains multiple constructors. The most commonly used is JobDetail (string name, string group, class jobclass). Jobpass is the class that implements the job interface, name is the name of the task in the scheduler, and group is the group name of the task in the scheduler. The default group name is Scheduler.DEFAULT_GROUP.
- Trigger: a class that describes the time rule that triggers job execution. contain:
SimpleTrigger: trigger rule for one time or fixed interval.
CronTrigger: describe more complex trigger rules through cron expressions.
Calendar: the calendar class provided by Quartz. Triggers can be associated with multiple calendars to exclude special dates.
Scheduler: represents a running container independent of Quartz. Trigger and JobDetail are registered in the scheduler. They have their own name (name) and group name (Group) in the scheduler. The combination of trigger and JobDetail name and group name must be unique, but the combination of trigger name and group name can be the same as JobDetail. A Job can be bound to multiple triggers or not.
Job also has a sub interface: stateful job, which is a label interface without methods, indicating stateful tasks.
- Stateless task: it has jobdatamap replication, so it can run concurrently;
- Stateful job: share a jobdatamap and save every modification to the jobdatamap. Therefore, the previous stateful job will block the next stateful job.
SimpleTrigger and CronTrigger
- SimpleTrigger can execute a Job task within a specified time period or multiple times within a time period.
- CronTrigger is very powerful. It schedules jobs based on Calendar and can specify intervals more accurately than simpletrigger. Therefore, crotrigger is more commonly used than simpletrigger. Crotrigger is based on cron expressions.
First, let's understand cron expression: the format of a string composed of seven sub expressions is as follows:
[seconds] [minutes] [hours] [days] [months] [weeks] [years]
For example: 00:00:00\* 10,11,12 1 # 5 2018 means 00:00:00 on the first Friday of October, November and December 2018. It doesn't seem easy to write and remember, but we can help us write expressions through the online cron expression generation tool on the network: the online cron expression generation tool: http: //cron.qqe2.com/

The meanings of special characters are as follows:
Asterisk (*): can be used in all fields to indicate each time in the corresponding time domain. For example, * in the minute field indicates "every minute";
Question mark (?): this character is used only in the date and week fields. It is usually specified as "meaningless value", which is equivalent to dot character;
Minus sign (-): indicates the range. If "10-12" is used in the hour field, it means 10 to 12, that is, 10, 11 and 12;
Comma (,): indicates the list value. If "Monday, Wednesday, Friday" is used in the week field, it means Monday, Wednesday and Friday;
Slash (/): X / Y represents an equal step sequence, where x is the starting value and Y is the incremental step value. If 0 / 15 is used in the minute field, it represents 0, 15, 30 and 45 seconds, while 5 / 15 represents 5, 20, 35 and 50 in the minute field. You can also use * / y, which is equivalent to 0 / y;
quartz dynamic timing task (database persistence)
preface
During project development, some scheduled tasks may not be needed after running for a period of time, or the execution time of scheduled tasks needs to be modified.
It is troublesome to modify the code and then repackage and release it. If you use Quartz to implement it, you don't need to modify the code to meet the requirements.
principle
- Use the API provided by quartz to complete the addition, deletion, modification and query of configuration tasks
- Save the configuration of the task in the database
to configure
application.yml
maven dependency package has been introduced above, which will not be repeated here. Add quartz configuration information directly under the spring attribute
spring: datasource: url: jdbc:mysql://192.168.161.3:3306/testdb?useUnicode=true&characterEncoding=utf-8&useSSL=false username: test password: 4rfv$RFV driver-class-name: com.mysql.jdbc.Driver quartz: job-store-type: JDBC #Database storage quartz task configuration jdbc: initialize-schema: NEVER #Automatically initialize the table structure. When it is started for the first time, always is written here
However, it may be a version bug. Sometimes automatic table creation will not take effect. Go to quartz-scheduler-x.x.x.jar to find the table creation sql script: classpath: org / quartz / impl / JDBC jobstore / tables_@@ Platform @ @. sql, and then execute.

Dynamic configuration code implementation
The first step is to create a scheduled task related entity class to save the scheduled task related information to the database
@Data public class QuartzBean { /** Task id */ private String id; /** Task name */ private String jobName; /** Task execution class */ private String jobClass; /** Task status start or pause*/ private Integer status; /** Task runtime expression */ private String cronExpression; }
Step 2: create a tool class for timed task suspension, modification, startup and single startup
public class QuartzUtils { /** * Create a scheduled task. The default startup status after a scheduled task is created * @param scheduler Scheduler * @param quartzBean Scheduled task information class */ @SuppressWarnings("unchecked") public static void createScheduleJob(Scheduler scheduler, QuartzBean quartzBean) throws ClassNotFoundException, SchedulerException { //The execution class obtained from the scheduled task must be the absolute path name of the class //The scheduled task class needs to be the concrete implementation of the job class. QuartzJobBean is the abstract class of the job. Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzBean.getJobClass()); // Build scheduled task information JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartzBean.getJobName()).build(); // Set scheduled task execution method CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression()); // Build trigger CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzBean.getJobName()).withSchedule(scheduleBuilder).build(); scheduler.scheduleJob(jobDetail, trigger); } /** * Pause scheduled task according to task name * @param scheduler Scheduler * @param jobName Scheduled task name */ public static void pauseScheduleJob(Scheduler scheduler, String jobName) throws SchedulerException { JobKey jobKey = JobKey.jobKey(jobName); scheduler.pauseJob(jobKey); } /** * Restore scheduled tasks according to task name * @param scheduler Scheduler * @param jobName Scheduled task name */ public static void resumeScheduleJob(Scheduler scheduler, String jobName) throws SchedulerException { JobKey jobKey = JobKey.jobKey(jobName); scheduler.resumeJob(jobKey); } /** * Run a scheduled task immediately according to the task name * @param scheduler Scheduler * @param jobName Scheduled task name */ public static void runOnce(Scheduler scheduler, String jobName) throws SchedulerException { JobKey jobKey = JobKey.jobKey(jobName); scheduler.triggerJob(jobKey); } /** * Update scheduled tasks * @param scheduler Scheduler * @param quartzBean Scheduled task information class */ public static void updateScheduleJob(Scheduler scheduler, QuartzBean quartzBean) throws SchedulerException { //Get the trigger of the corresponding task TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName()); //Set scheduled task execution method CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression()); //Rebuild trigger for task CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); //Reset corresponding job scheduler.rescheduleJob(triggerKey, trigger); } /** * Delete the scheduled task from the scheduler according to the scheduled task name * @param scheduler Scheduler * @param jobName Scheduled task name */ public static void deleteScheduleJob(Scheduler scheduler, String jobName) throws SchedulerException { JobKey jobKey = JobKey.jobKey(jobName); scheduler.deleteJob(jobKey); } }
Control layer
@Controller @RequestMapping("/quartz/job/") public class QuartzController { //Injection task scheduling @Resource private Scheduler scheduler; @PostMapping("/create") @ResponseBody public String createJob(@RequestBody QuartzBean quartzBean) throws SchedulerException, ClassNotFoundException { QuartzUtils.createScheduleJob(scheduler,quartzBean); return "Task created";//return here is not a production level code. Write the test simply } @PostMapping("/pause") @ResponseBody public String pauseJob(String jobName) throws SchedulerException { QuartzUtils.pauseScheduleJob (scheduler,jobName); return "Paused successfully";//return here is not a production level code. Write the test simply } @PostMapping("/run") @ResponseBody public String runOnce(String jobName) throws SchedulerException { QuartzUtils.runOnce (scheduler,jobName); return "Run task" + jobName + "success";//return here is not a production level code. Write the test simply } @PostMapping("/resume") @ResponseBody public String resume(String jobName) throws SchedulerException { QuartzUtils.resumeScheduleJob(scheduler,jobName); return "Resume scheduled task succeeded:" + jobName; } @PostMapping("/update") @ResponseBody public String update(@RequestBody QuartzBean quartzBean) throws SchedulerException { QuartzUtils.updateScheduleJob(scheduler,quartzBean); return "The scheduled task scheduling information was updated successfully"; } }
Distributed task scheduling framework - XXL job
New version of XXL-JOB distributed timing framework SrpingBoot-XXL-JOB