4

In the previous article, we introduced how to use Spring Boot's built-in @Scheduled annotation to realize the timing task . The limitations of this approach are also mentioned at the end of the article. When in a cluster environment, if the execution or operation of the task relies on some shared resources, there will be competition. If you do not introduce mechanisms such as distributed locks for scheduling, unexpected execution results may occur. Therefore, the @Scheduled annotation is more reasonable for some timed tasks related to the maintenance of the single instance itself, such as regularly cleaning files in a certain directory of the service instance, and regularly uploading some statistical data of this instance.

So, when the business logic is actually implemented, there is no better timing task plan? Today we will introduce an old distributed timing task framework, a use case under Spring Boot.

Elasitc Job

The predecessor of Elastic Job was Dangdang's open source distributed task scheduling framework, and it has now joined the Apache Foundation.

There are two branches under this project: ElasticJob-Lite and ElasticJob-Cloud. ElasticJob-Lite is a lightweight task management solution. The following cases in this article will use this to achieve. and
ElasticJob-Cloud is relatively heavier because it uses containers to manage tasks and isolate resources.

For more information about ElasticJob, you can also to go directly to the official website for more information.

Try it

Say so much, let's try it together!

first step : Create a basic Spring Boot project, if not yet? Then take a look at this Quick Start .

Step : pom.xml added starter elasticjob-lite of

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

    // ...
</dependencies>

third step : 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());
    }

}

Fourth step : 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-center , mainly configure the registration center and namespace of elastic job

The second part: task configuration, elasticjob.jobs , where my-simple-job is the name of the task, you can name it according to your preference, but don't repeat it. The configuration elastic-job-class under the task is the implementation class of the task, cron is the execution regular expression, and sharding-total-count is the total number of task fragments. We can use this parameter to split the task and achieve parallel processing. Set it to 1 here first, and we will talk about the use of fragmentation later.

Run and test

When all the above operations are completed, we can try to run the above application, because ZooKeeper is needed to coordinate task scheduling in a distributed environment. So, you need to install ZooKeeper locally and then start it. Note: The above elasticjob.reg-center.server-lists configuration should be modified according to the ZooKeeper address and port you actually use.

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

2021-07-20 15:33:39.541  INFO 56365 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler 'my-simple-job' initialized from an externally provided properties instance.
2021-07-20 15:33:39.541  INFO 56365 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler version: 2.3.2
2021-07-20 15:33:39.551  INFO 56365 --- [           main] org.apache.curator.utils.Compatibility   : Using org.apache.zookeeper.server.quorum.MultipleAddresses
2021-07-20 15:33:40.067  INFO 56365 --- [           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 --- [           main] org.quartz.core.QuartzScheduler          : Scheduler my-simple-job_$_NON_CLUSTERED started.
2021-07-20 15:33:40.078  INFO 56365 --- [           main] .s.b.j.ScheduleJobBootstrapStartupRunner : ElasticJob Bootstrap started.
2021-07-20 15:33:45.157  INFO 56365 --- [le-job_Worker-1] com.didispace.chapter72.MySimpleJob      : MySimpleJob start : didispace.com 1626766425157
2021-07-20 15:33:50.010  INFO 56365 --- [le-job_Worker-1] com.didispace.chapter72.MySimpleJob      : MySimpleJob start : didispace.com 1626766430010
2021-07-20 15:33:55.013  INFO 56365 --- [le-job_Worker-1] com.didispace.chapter72.MySimpleJob      : MySimpleJob start : didispace.com 1626766435013

Since it is a distributed task scheduling, let's start another one (note that when the same machine is started, there will be port conflicts, you can add -Dserver.port=8081 to the start command to distinguish the ports), and the second started service log is also printed Similar content

2021-07-20 15:34:06.430  INFO 56371 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler 'my-simple-job' initialized from an externally provided properties instance.
2021-07-20 15:34:06.430  INFO 56371 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler version: 2.3.2
2021-07-20 15:34:06.436  INFO 56371 --- [           main] org.apache.curator.utils.Compatibility   : Using org.apache.zookeeper.server.quorum.MultipleAddresses
2021-07-20 15:34:06.786  INFO 56371 --- [           main] c.d.chapter72.Chapter72Application       : Started Chapter72Application in 1.446 seconds (JVM running for 1.884)
2021-07-20 15:34:06.787  INFO 56371 --- [           main] .s.b.j.ScheduleJobBootstrapStartupRunner : Starting ElasticJob Bootstrap.
2021-07-20 15:34:06.792  INFO 56371 --- [           main] org.quartz.core.QuartzScheduler          : Scheduler my-simple-job_$_NON_CLUSTERED started.
2021-07-20 15:34:06.792  INFO 56371 --- [           main] .s.b.j.ScheduleJobBootstrapStartupRunner : ElasticJob Bootstrap started.
2021-07-20 15:34:10.182  INFO 56371 --- [le-job_Worker-1] com.didispace.chapter72.MySimpleJob      : MySimpleJob start : didispace.com 1626766450182
2021-07-20 15:34:15.010  INFO 56371 --- [le-job_Worker-1] com.didispace.chapter72.MySimpleJob      : MySimpleJob start : didispace.com 1626766455010
2021-07-20 15:34:20.013  INFO 56371 --- [le-job_Worker-1] com.didispace.chapter72.MySimpleJob      : MySimpleJob start : didispace.com 1626766460013

At this point, after looking back at the first application started before, the log output stopped. Since we set the total number of shards to 1, after this task is started, only one instance will take over the execution. This avoids the problem of multiple simultaneous and repeated execution of the same logic. At the same time, this also supports the high availability of task execution. For example: you can try to terminate the second started application (which is printing logs). It can be found that the first application started (which has stopped outputting the log before) continues to print the task log.

In the entire implementation process, we did not manually write any distributed locks and other codes to implement the task scheduling logic. We only need to pay attention to the task logic itself, and then control the division of the task by configuring the fragmentation method, which can be easily Realize timing task management in distributed cluster environment. Is it more convenient to implement this method in complex scenarios than @Scheduled

Remember to write by yourself, so that you will have a deeper experience! If you encounter a problem, you can pull the code example at the end of the article to compare whether there is a difference in configuration. In the next article, we will continue to introduce some advanced content about timed tasks. If you are interested in this content, you can bookmark this series of tutorial "Spring Boot 2.x Basic Tutorial" and click directly! . If you encounter difficulties in the learning process, you can join our Spring technical exchange group , participate in exchanges and discussions, and better study and progress!

Code example

The complete project of this article can be viewed in the chapter7-2 directory in the following warehouse:

If you think this article is good, welcome Star support, your attention is my motivation for persistence!

Welcome to pay attention to my public account: Program Ape DD, share knowledge and thoughts that can’t be seen elsewhere

程序猿DD
2.2k 声望2.8k 粉丝

作品:《Spring Cloud微服务实战》、SpringForAll社区、OpenWrite、Youtube中文配音