Spring Boot 2.x基礎(chǔ)教程:使用Elastic Job實(shí)現(xiàn)定時(shí)任務(wù)

上一篇,我們介紹了如何使用Spring Boot自帶的@Scheduled注解實(shí)現(xiàn)定時(shí)任務(wù)(https://blog.didispace.com/spring-boot-learning-2-7-1/)。文末也提及了這種方式的局限性。當(dāng)在集群環(huán)境下的時(shí)候,如果任務(wù)的執(zhí)行或操作依賴一些共享資源的話,就會(huì)存在競(jìng)爭(zhēng)關(guān)系。如果不引入分布式鎖等機(jī)制來(lái)做調(diào)度的話,就可能出現(xiàn)預(yù)料之外的執(zhí)行結(jié)果。所以,@Scheduled注解更偏向于使用在單實(shí)例自身維護(hù)相關(guān)的一些定時(shí)任務(wù)上會(huì)更為合理一些,比如:定時(shí)清理服務(wù)實(shí)例某個(gè)目錄下的文件、定時(shí)上傳本實(shí)例的一些統(tǒng)計(jì)數(shù)據(jù)等。
那么,在實(shí)際實(shí)現(xiàn)業(yè)務(wù)邏輯的時(shí)候,沒(méi)有更好的定時(shí)任務(wù)方案呢?今天我們就來(lái)介紹一個(gè)老牌的分布式定時(shí)任務(wù)框架,在Spring Boot下的使用案例。
Elastic Job
Elastic Job的前生是當(dāng)當(dāng)開源的一款分布式任務(wù)調(diào)度框架,而目前已經(jīng)加入到了Apache基金會(huì)。
該項(xiàng)目下有兩個(gè)分支:ElasticJob-Lite和ElasticJob-Cloud。ElasticJob-Lite是一個(gè)輕量級(jí)的任務(wù)管理方案,本文接下來(lái)的案例就用這個(gè)來(lái)實(shí)現(xiàn)。而 ElasticJob-Cloud則相對(duì)重一些,因?yàn)樗褂萌萜鱽?lái)管理任務(wù)和隔離資源。
更多關(guān)于ElasticJob的介紹,您也可以點(diǎn)擊這里直達(dá)官方網(wǎng)站(https://shardingsphere.apache.org/elasticjob/)了解更多信息。
動(dòng)手試試
說(shuō)那么多,一起動(dòng)手試試吧!
第一步:創(chuàng)建一個(gè)最基礎(chǔ)的Spring Boot項(xiàng)目,如果還不會(huì)?那么看看這篇快速入門(https://blog.didispace.com/spring-boot-learning-21-1-1/)。
第二步:pom.xml中添加elasticjob-lite的starter
<dependencies>
<dependency>
<groupId>org.apache.shardingsphere.elasticjob</groupId>
<artifactId>elasticjob-lite-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
// ...
</dependencies>
第三步:創(chuàng)建一個(gè)簡(jiǎn)單任務(wù)
@Slf4j
@Service
public class MySimpleJob implements SimpleJob {
@Override
public void execute(ShardingContext context) {
log.info("MySimpleJob start : didispace.com {}", System.currentTimeMillis());
}
}
第四步:編輯配置文件
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
這里主要有兩個(gè)部分:
第一部分:elasticjob.reg-center開頭的,主要配置elastic job的注冊(cè)中心和namespace
第二部分:任務(wù)配置,以elasticjob.jobs開頭,這里的my-simple-job是任務(wù)的名稱,根據(jù)你的喜好命名即可,但不要重復(fù)。任務(wù)的下的配置elastic-job-class是任務(wù)的實(shí)現(xiàn)類,cron是執(zhí)行規(guī)則表達(dá)式,sharding-total-count是任務(wù)分片的總數(shù)。我們可以通過(guò)這個(gè)參數(shù)來(lái)把任務(wù)切分,實(shí)現(xiàn)并行處理。這里先設(shè)置為1,后面我們另外講分片的使用。
運(yùn)行與測(cè)試
完成了上面所有操作時(shí)候,我們可以嘗試運(yùn)行一下上面應(yīng)用,因?yàn)檫@里需要用到ZooKeeper來(lái)協(xié)調(diào)分布式環(huán)境下的任務(wù)調(diào)度。所以,你需要先在本地安裝ZooKeeper,然后啟動(dòng)它。注意:上面elasticjob.reg-center.server-lists配置,根據(jù)你實(shí)際使用的ZooKeeper地址和端口做相應(yīng)修改。
在啟動(dòng)上述Spring Boot應(yīng)用之后,我們可以看到如下日志輸出:
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
既然是分布式任務(wù)調(diào)度,那么我們?cè)賳?dòng)一個(gè)(注意,在同一臺(tái)機(jī)器啟動(dòng)的時(shí)候,會(huì)端口沖突,可以在啟動(dòng)命令中加入-Dserver.port=8081來(lái)區(qū)分端口),在第二個(gè)啟動(dòng)的服務(wù)日志也打印了類似的內(nèi)容
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
此時(shí),在回頭看看之前第一個(gè)啟動(dòng)的應(yīng)用,日志輸出停止了。由于我們?cè)O(shè)置了分片總數(shù)為1,所以這個(gè)任務(wù)啟動(dòng)之后,只會(huì)有一個(gè)實(shí)例接管執(zhí)行。這樣就避免了多個(gè)進(jìn)行同時(shí)重復(fù)的執(zhí)行相同邏輯而產(chǎn)生問(wèn)題的情況。同時(shí),這樣也支持了任務(wù)執(zhí)行的高可用。比如:可以嘗試把第二個(gè)啟動(dòng)的應(yīng)用(正在打印日志的)終止掉??梢园l(fā)現(xiàn),第一個(gè)啟動(dòng)的應(yīng)用(之前已經(jīng)停止輸出日志)繼續(xù)開始打印任務(wù)日志了。
在整個(gè)實(shí)現(xiàn)過(guò)程中,我們并沒(méi)有自己手工的去編寫任何的分布式鎖等代碼去實(shí)現(xiàn)任務(wù)調(diào)度邏輯,只需要關(guān)注任務(wù)邏輯本身,然后通過(guò)配置分片的方式來(lái)控制任務(wù)的分割,就可以輕松的實(shí)現(xiàn)分布式集群環(huán)境下的定時(shí)任務(wù)管理了。是不是在復(fù)雜場(chǎng)景下,這種方式實(shí)現(xiàn)起來(lái)要比@Scheduled更方便呢?
記得自己動(dòng)手寫一寫,這樣體會(huì)更深哦!如果碰到問(wèn)題,可以拉取文末的代碼示例對(duì)比一下是否有地方配置不一樣。下一篇,我們還將繼續(xù)介紹關(guān)于定時(shí)任務(wù)的一些高級(jí)內(nèi)容。關(guān)注我,收藏本系列教程《Spring Boot 2.x基礎(chǔ)教程》點(diǎn)擊直達(dá)!(http://blog.didispace.com/spring-boot-learning-2x/)。
學(xué)習(xí)過(guò)程中如遇困難,加入Spring技術(shù)交流群,參與討論
關(guān)注我回復(fù)「加群」,加入Spring技術(shù)交流群
代碼示例
本文的完整工程可以查看下面?zhèn)}庫(kù)中的chapter7-2目錄:
Github:https://github.com/dyc87112/SpringBoot-Learning/tree/master/2.x Gitee:https://gitee.com/didispace/SpringBoot-Learning/tree/master/2.x
如果您覺得本文不錯(cuò),歡迎Star支持,您的關(guān)注是我堅(jiān)持的動(dòng)力!
往期推薦
