Spring Boot 2.x基礎教程:使用Elastic Job實現定時任務

上一篇,咱們介紹瞭如何使用Spring Boot自帶的@Scheduled註解實現定時任務。文末也說起了這種方式的侷限性。當在集羣環境下的時候,若是任務的執行或操做依賴一些共享資源的話,就會存在競爭關係。若是不引入分佈式鎖等機制來作調度的話,就可能出現預料以外的執行結果。因此,@Scheduled註解更偏向於使用在單實例自身維護相關的一些定時任務上會更爲合理一些,好比:定時清理服務實例某個目錄下的文件、定時上傳本實例的一些統計數據等。html

那麼,在實際實現業務邏輯的時候,沒有更好的定時任務方案呢?今天咱們就來介紹一個老牌的分佈式定時任務框架,在Spring Boot下的使用案例。java

Elasitc Job

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,分享外面看不到的乾貨與思考!

相關文章
相關標籤/搜索