In the previous article, we showed you how to implement timed tasks using the @Scheduled annotation that comes with Spring Boot. The limitations of this approach are also mentioned at the end of the article. In a clustered environment, there will be competition if the execution or operation of a task depends on some shared resource. If a mechanism such as distributed locking is not introduced for scheduling, unexpected execution results may occur. Therefore, the @Scheduled annotation is more appropriate for the maintenance of the single instance itself, such as cleaning files from a directory of the service instance on a regular basis, and uploading statistics from the instance on a regular basis.

So, what better way to schedule tasks when actually implementing the business logic? Today we will introduce an old distributed timed task framework, under the use of Spring Boot case.

Elasitc Job

Elastic Job started as an open source distributed task scheduling framework from Dangdang and is now part of the Apache Foundation.

There are two branches under this project: ElasticJob-Lite and ElasticJob-Cloud. ElasticJob-Lite is a lightweight task management scheme, which is used in the following example. ElasticJob-Cloud is heavier because it uses a container to manage tasks and isolate resources.

For more information on ElasticJob, you can go to the official ElasticJob website here.

Began to try

Say so much, try it together!

Step 1: Create a basic Spring Boot project, if you don’t already know it? So check out this quick primer.

Step 2: Add Starter for ElasticJob-Lite to Pom.xml

<dependencies> <dependency> <groupId>org.apache.shardingsphere.elasticjob</groupId> <artifactId elasticjob-lite-spring-boot-starter</artifactId> <version>3.0.0</version> </dependency> //... </dependencies>

Step 3: Create a simple task

@Slf4j
@Service
public class MySimpleJob implements SimpleJob {

    @Override
    public void execute(ShardingContext context) {
        log.info("MySimpleJob start : didispace.com {}", System.currentTimeMillis());
    }

}

Step 4: Edit the configuration file

elasticjob.reg-center.server-lists=localhost:2181
elasticjob.reg-center.namespace=didispace

elasticjob.jobs.my-simple-job.elastic-job-class=com.didispace.chapter72.MySimpleJob
elasticjob.jobs.my-simple-job.cron=0/5 * * * * ?
elasticjob.jobs.my-simple-job.sharding-total-count=1

There are two main parts here:

The first part: elasticjob. Reg – starting center, the main configuration elastic job registry and namespace

. The second part: the job configuration to elasticjob jobs at the beginning, my – simple – job here is the name of the task, the be fond of according to your name, but don’t repeat. The configuration under the task Elastic-job-class is the implementation class of the task, cron is the execution rule expression, and sharding-total-count is the total number of task shards. We can use this parameter to divide the tasks and achieve parallel processing. I’m going to set it to 1, and we’ll talk about how to use sharding later.

Run and Test

Once you’ve done all of this, you can try running the above application because you need to use ZooKeeper to coordinate task scheduling in a distributed environment. So, you need to install ZooKeeper locally and then start it. Note: ElasticJob.reg-center-server-lists is not an elasticJob.reg-center-server-lists configuration, but it is not an elasticJob.reg-center-server-lists configuration.

After starting the Spring Boot application above, we can see the following log output:

The 2021-07-20 15:33:39. 56365-541 the INFO [main] org. Quartz. Impl. StdSchedulerFactory: Quartz scheduler 'my-simple-job' initialized from an unbonded provided properties instance. 2021-07-20 15:33:39.541 INFO 56365 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: The 2021-07-20 15:33:39 2.3.2. 56365-551 the INFO [main] org.apache.curator.utils.Com patibility: Using org. Apache. Zookeeper. Server. Quorum. MultipleAddresses 15:33:40 2021-07-20. 56365-067 the INFO [main] c.d.chapter72.Chapter72Application : Started Chapter72Application in 3.25 seconds (JVM running for 4.965) 2021-07-20 15:33:40.069 INFO 56365 -- [Main] .s.b.j.ScheduleJobBootstrapStartupRunner : Starting ElasticJob Bootstrap. 2021-07-20 15:33:40. 078 INFO 56365 - [the main] org. Quartz. Core. QuartzScheduler: Scheduler my-simple-clustered $_NON_CLUSTERED started. 2021-07-20 15:33:40.078 INFO 56365 -- [Main] Scheduler my-simple-clustered $_NON_CLUSTERED started. Scheduler my-simple-clustered $_NON_CLUSTERED started. .s.b.j.ScheduleJobBootstrapStartupRunner : 2021-07-20 15:32:42.57 INFO 665 -- [EL-JOB_WORKING-1] com.didispace.chapter72.MySimpleJob : MySimpleJob start : Didispace.com 1626766425157 15:33:50 2021-07-20. 56365-010 the INFO] [le - job_Worker - 1 com. Didispace. Chapter72. MySimpleJob  : MySimpleJob start : Didispace.com 1626766430010 15:33:55 2021-07-20. 56365-013 the INFO] [le - job_Worker - 1 com. Didispace. Chapter72. MySimpleJob  : MySimpleJob start : didispace.com 1626766435013

Since this is distributed task scheduling, we start another one (note that when the same machine is started, there will be port conflict, you can add -dserver. port=8081 in the startup command to distinguish the ports), and the service log on the second start also prints a similar content

The 2021-07-20 15:34:06. 56371-430 the INFO [main] org. Quartz. Impl. StdSchedulerFactory: Quartz scheduler 'my-simple-job' initialized from an unbonded provided properties instance. 2021-07-20 15:34:06.430 INFO 56371 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: The 2021-07-20 15:34:06 2.3.2. 56371-436 the INFO [main] org.apache.curator.utils.Com patibility: Using org. Apache. Zookeeper. Server. Quorum. MultipleAddresses 15:34:06 2021-07-20. 56371-786 the INFO [main] c.d.chapter72.Chapter72Application : Started Chapter72Application in 1.446 seconds (JVM running for 1.884 seconds) 2021-07-20 15:34:06.87 INFO 5667 -- -- [Main] .s.b.j.ScheduleJobBootstrapStartupRunner : Starting ElasticJob Bootstrap. 2021-07-20 15:34:06. 792 INFO 56371 - [the main] org. Quartz. Core. QuartzScheduler: Scheduler my-simple-jost_$_NON_CLUSTERED Started. 2021-07-20 15:34:06.792 INFO 56371 -- -- [Main] Scheduler my-simple-jost_$_NON_CLUSTERED Started. Scheduler my-simple-jost_$_NON_CLUSTERED Started. .s.b.j.ScheduleJobBootstrapStartupRunner : Started. 2021-07-20 15:34:02.182 INFO 667 -- -- [ElasticJob Bootstrap Started. com.didispace.chapter72.MySimpleJob : MySimpleJob start : Didispace.com 1626766450182 15:34:15 2021-07-20. 56371-010 the INFO] [le - job_Worker - 1 com. Didispace. Chapter72. MySimpleJob  : MySimpleJob start : Didispace.com 1626766455010 15:34:20 2021-07-20. 56371-013 the INFO] [le - job_Worker - 1 com. Didispace. Chapter72. MySimpleJob  : MySimpleJob start : didispace.com 1626766460013

At this point, the log output stops before looking back at the first application started. Since we set the total number of shards to 1, only one instance will take over after this task starts. This avoids the problem of multiple simultaneous and repetitive implementations of the same logic. At the same time, this also enables high availability for task execution. For example, you could try to terminate the second application that started (the one printing logs). You can see that the first application started (which had previously stopped printing the log) continues to print the task log.

In the whole implementation process, we did not manually write any distributed lock code to realize the task scheduling logic, we only need to pay attention to the task logic itself, and then control the task segmentation by configuring the way of shard, then we can easily realize the timing task management in the distributed cluster environment. Is this more convenient than @scheduledfor complex scenarios?

Remember to write by yourself, so that the experience is deeper Oh! If you run into problems, pull up the code sample at the end of this article and see if anything is configured differently. In the next article, we will continue to cover some advanced topics about timed tasks. If you are interested in this tutorial, you can bookmark the Spring Boot 2.x Fundamentals tutorial series by clicking on it! . If you encounter difficulties in the learning process, you can join our Spring technology exchange group to participate in the communication and discussion, for better learning and progress!

Code sample

The complete project for this article can be viewed in the chapter7-2 directory in the repository below:

  • Github:https://github.com/dyc87112/SpringBoot-Learning/
  • Gitee:https://gitee.com/didispace/SpringBoot-Learning/

If you find this article good, welcomeStarSupport, your attention is the power I adhere to!

Welcome to pay attention to my official account: program ape DD, to share the knowledge and thinking that can not be seen elsewhere