上一篇,咱們介紹瞭如何使用Spring Boot自帶的@Scheduled
註解實現定時任務。文末也說起了這種方式的侷限性。當在集羣環境下的時候,若是任務的執行或操做依賴一些共享資源的話,就會存在競爭關係。若是不引入分佈式鎖等機制來作調度的話,就可能出現預料以外的執行結果。因此,@Scheduled
註解更偏向於使用在單實例自身維護相關的一些定時任務上會更爲合理一些,好比:定時清理服務實例某個目錄下的文件、定時上傳本實例的一些統計數據等。html
那麼,在實際實現業務邏輯的時候,沒有更好的定時任務方案呢?今天咱們就來介紹一個老牌的分佈式定時任務框架,在Spring Boot下的使用案例。java
Elastic Job的前生是噹噹開源的一款分佈式任務調度框架,而目前已經加入到了Apache基金會。git
該項目下有兩個分支:ElasticJob-Lite和ElasticJob-Cloud。ElasticJob-Lite是一個輕量級的任務管理方案,本文接下來的案例就用這個來實現。而
ElasticJob-Cloud則相對重一些,由於它使用容器來管理任務和隔離資源。github
更多關於ElasticJob的介紹,您也能夠點擊這裏直達官方網站瞭解更多信息。spring
說那麼多,一塊兒動手試試吧!apache
第一步:建立一個最基礎的Spring Boot項目,若是還不會?那麼看看這篇快速入門。框架
第二步: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>
第三步:建立一個簡單任務ide
@Slf4j @Service public class MySimpleJob implements SimpleJob { @Override public void execute(ShardingContext context) { log.info("MySimpleJob start : didispace.com {}", System.currentTimeMillis()); } }
第四步:編輯配置文件spring-boot
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
這裏主要有兩個部分:
第一部分:elasticjob.reg-center
開頭的,主要配置elastic job的註冊中心和namespace
第二部分:任務配置,以elasticjob.jobs
開頭,這裏的my-simple-job
是任務的名稱,根據你的喜愛命名便可,但不要重複。任務的下的配置elastic-job-class
是任務的實現類,cron
是執行規則表達式,sharding-total-count
是任務分片的總數。咱們能夠經過這個參數來把任務切分,實現並行處理。這裏先設置爲1,後面咱們另外講分片的使用。
完成了上面全部操做時候,咱們能夠嘗試運行一下上面應用,由於這裏須要用到ZooKeeper來協調分佈式環境下的任務調度。因此,你須要先在本地安裝ZooKeeper,而後啓動它。注意:上面elasticjob.reg-center.server-lists
配置,根據你實際使用的ZooKeeper地址和端口作相應修改。
在啓動上述Spring Boot應用以後,咱們能夠看到以下日誌輸出:
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
既然是分佈式任務調度,那麼咱們再啓動一個(注意,在同一臺機器啓動的時候,會端口衝突,能夠在啓動命令中加入-Dserver.port=8081
來區分端口),在第二個啓動的服務日誌也打印了相似的內容
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
此時,在回頭看看以前第一個啓動的應用,日誌輸出中止了。因爲咱們設置了分片總數爲1,因此這個任務啓動以後,只會有一個實例接管執行。這樣就避免了多個進行同時重複的執行相同邏輯而產生問題的狀況。同時,這樣也支持了任務執行的高可用。好比:能夠嘗試把第二個啓動的應用(正在打印日誌的)終止掉。能夠發現,第一個啓動的應用(以前已經中止輸出日誌)繼續開始打印任務日誌了。
在整個實現過程當中,咱們並無本身手工的去編寫任何的分佈式鎖等代碼去實現任務調度邏輯,只須要關注任務邏輯自己,而後經過配置分片的方式來控制任務的分割,就能夠輕鬆的實現分佈式集羣環境下的定時任務管理了。是否是在複雜場景下,這種方式實現起來要比@Scheduled
更方便呢?
記得本身動手寫一寫,這樣體會更深哦!若是碰到問題,能夠拉取文末的代碼示例對比一下是否有地方配置不同。下一篇,咱們還將繼續介紹關於定時任務的一些高級內容。若是您對這個內容感興趣,能夠收藏本系列教程《Spring Boot 2.x基礎教程》點擊直達!。學習過程當中如遇困難,能夠加入咱們的Spring技術交流羣,參與交流與討論,更好的學習與進步!
本文的完整工程能夠查看下面倉庫中的chapter7-2
目錄:
若是您以爲本文不錯,歡迎Star
支持,您的關注是我堅持的動力!
歡迎關注個人公衆號:程序猿DD,分享外面看不到的乾貨與思考!