javascript
第四十章:基于SpringBoot Quartz完成定时任务分布式多节点负载持久化
在上一章【第三十九章:基于SpringBoot & Quartz完成定時任務分布式單節點持久化】中我們已經完成了任務的持久化,當我們創建一個任務時任務會被quartz定時任務框架自動持久化到數據庫,我們采用的是SpringBoot項目托管的dataSource來完成的數據源提供,當然也可以使用quartz內部配置數據源方式,我們的標題既然是提到了定時任務的分布式多節點,那么怎么才算是多節點呢?當有節點故障或者手動停止運行后是否可以自動漂移任務到可用的分布式節點呢?
本章目標
構建項目
注意:我們本章項目需要結合上一章共同完成,有一點要注意的是任務在持久化到數據庫內時會保存任務的全路徑,如:com.hengyu.chapter39.timers.GoodStockCheckTimer ,quartz在運行任務時會根據任務全路徑去執行,如果不一致則會提示找不到指定類,我們本章在創建項目時package需要跟上一章完全一致。
我們這里就不去直接創建新項目了,直接復制上一章項目的源碼為新的項目命名為Chapter40
配置分布式
在上一章配置文件quartz.properties中我們其實已經為分布式做好了相關配置,下面我們就來看一下分布式相關的配置。
分布式相關配置:
1. org.quartz.scheduler.instanceId : 定時任務的實例編號,如果手動指定需要保證每個節點的唯一性,因為quartz不允許出現兩個相同instanceId的節點,我們這里指定為Auto就可以了,我們把生成編號的任務交給quartz。
2. org.quartz.jobStore.isClustered: 這個屬性才是真正的開啟了定時任務的分布式配置,當我們配置為true時quartz框架就會調用ClusterManager來初始化分布式節點。
3. org.quartz.jobStore.clusterCheckinInterval:配置了分布式節點的檢查時間間隔,單位:毫秒。
下面是quartz.properties配置文件配置信息:
當我們啟動任務節點時,會根據org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread屬性配置進行是否自動加載任務,默認true自動加載數據庫內的任務到節點。
測試分布式
上一章項目節點名稱:quartz-cluster-node-first
本章項目節點名稱:quartz-cluster-node-second
由于我們quartz-cluster-node-first的商品庫存檢查定時任務是每隔30秒執行一次,所以任務除非手動清除否則是不會被清空的,在運行項目測試之前需要將application.yml配置文件的端口號、項目名稱修改下,保證quartz-cluster-node-second與quartz-cluster-node-first端口號不一致,可以同時運行,修改后為:
spring:application:name: quzrtz-cluster-node-second server:port: 8082復制代碼然后修改相應控制臺輸出,為了能夠區分任務執行者是具體的節點。
Chapter40Application啟動類修改日志輸出: logger.info("【【【【【【定時任務分布式節點 - quartz-cluster-node-second 已啟動】】】】】】");GoodAddTimer商品添加任務類修改日志輸出: logger.info("分布式節點quartz-cluster-node-second,商品添加完成后執行任務,任務時間:{}",new Date());GoodStockCheckTimer商品庫存檢查任務類修改日志輸出: logger.info("分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:{}",new Date());復制代碼下面我們啟動本章項目,查看控制臺輸出內容,如下所示:
2017-11-12 10:28:39.969 INFO 11048 --- [ main] c.hengyu.chapter39.Chapter40Application : 【【【【【【定時任務分布式節點 - quartz-cluster-node-second 已啟動】】】】】】 2017-11-12 10:28:41.930 INFO 11048 --- [lerFactoryBean]] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now, after delay of 2 seconds 2017-11-12 10:28:41.959 INFO 11048 --- [lerFactoryBean]] org.quartz.core.QuartzScheduler : Scheduler schedulerFactoryBean_$_yuqiyu1510453719308 started. 2017-11-12 10:28:51.963 INFO 11048 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: detected 1 failed or restarted instances. 2017-11-12 10:28:51.963 INFO 11048 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: Scanning for instance "yuqiyu1510450938654"'s failed in-progress jobs. 2017-11-12 10:28:51.967 INFO 11048 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: ......Freed 1 acquired trigger(s). 2017-11-12 10:29:00.024 INFO 11048 --- [ryBean_Worker-1] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 10:29:00 CST 2017復制代碼可以看到項目啟動完成后自動分配的instanceId為yuqiyu1510450938654,生成的規則是當前用戶的名稱+時間戳。然后ClusterManager分布式管理者自動介入進行掃描是否存在匹配的觸發器任務,如果存在則會自動執行任務邏輯,而商品庫存檢查定時任務確實由quartz-cluster-node-second進行輸出的。
測試任務自動漂移
下面我們也需要把quartz-cluster-node-first的輸出進行修改,如下所示:
Chapter39Application啟動類修改日志輸出: logger.info("【【【【【【定時任務分布式節點 - quartz-cluster-node-first 已啟動】】】】】】");GoodAddTimer商品添加任務類修改日志輸出: logger.info("分布式節點quartz-cluster-node-first,商品添加完成后執行任務,任務時間:{}",new Date());GoodStockCheckTimer商品庫存檢查任務類修改日志輸出: logger.info("分布式節點quartz-cluster-node-first,執行庫存檢查定時任務,執行時間:{}",new Date());復制代碼接下來我們啟動quartz-cluster-node-first,并查看控制臺的輸出內容:
2017-11-12 10:34:09.750 INFO 192 --- [ main] c.hengyu.chapter39.Chapter39Application : 【【【【【【定時任務分布式節點 - quartz-cluster-node-first 已啟動】】】】】】 2017-11-12 10:34:11.690 INFO 192 --- [lerFactoryBean]] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now, after delay of 2 seconds 2017-11-12 10:34:11.714 INFO 192 --- [lerFactoryBean]] org.quartz.core.QuartzScheduler : Scheduler schedulerFactoryBean_$_yuqiyu1510454049066 started.復制代碼項目啟動完成后,定時節點并沒有實例化ClusterManager來完成分布式節點的初始化,因為quartz檢測到有其他的節點正在處理任務,這樣也是保證了任務執行的唯一性。
關閉quartz-cluster-node-second
我們關閉quartz-cluster-node-second運行的項目,預計的目的可以達到quartz-cluster-node-first會自動接管數據庫內的任務,完成任務執行的自動漂移,我們來查看quartz-cluster-node-first的控制臺輸出內容:
2017-11-12 10:34:09.750 INFO 192 --- [ main] c.hengyu.chapter39.Chapter39Application : 【【【【【【定時任務分布式節點 - quartz-cluster-node-first 已啟動】】】】】】 2017-11-12 10:34:11.690 INFO 192 --- [lerFactoryBean]] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now, after delay of 2 seconds 2017-11-12 10:34:11.714 INFO 192 --- [lerFactoryBean]] org.quartz.core.QuartzScheduler : Scheduler schedulerFactoryBean_$_yuqiyu1510454049066 started. 2017-11-12 10:41:11.793 INFO 192 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: detected 1 failed or restarted instances. 2017-11-12 10:41:11.793 INFO 192 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: Scanning for instance "yuqiyu1510453719308"'s failed in-progress jobs. 2017-11-12 10:41:11.797 INFO 192 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: ......Freed 1 acquired trigger(s). 2017-11-12 10:41:11.834 INFO 192 --- [ryBean_Worker-1] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-first,執行庫存檢查定時任務,執行時間:Sun Nov 12 10:41:11 CST 2017復制代碼控制臺已經輸出了持久的定時任務,輸出節點是quartz-cluster-node-first,跟我們預計的一樣,節點quartz-cluster-node-first完成了自動接管quartz-cluster-node-second的工作,而這個過程肯定有一段時間間隔,而這個間隔可以修改quartz.properties配置文件內的屬性org.quartz.jobStore.clusterCheckinInterval進行調節。
關閉quartz-cluster-node-first
我們同樣可以測試啟動任務節點quartz-cluster-node-second后,再關閉quartz-cluster-node-first任務節點,查看quartz-cluster-node-second控制臺的輸出內容:
2017-11-12 10:53:31.010 INFO 3268 --- [ main] c.hengyu.chapter39.Chapter40Application : 【【【【【【定時任務分布式節點 - quartz-cluster-node-second 已啟動】】】】】】 2017-11-12 10:53:32.967 INFO 3268 --- [lerFactoryBean]] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now, after delay of 2 seconds 2017-11-12 10:53:32.992 INFO 3268 --- [lerFactoryBean]] org.quartz.core.QuartzScheduler : Scheduler schedulerFactoryBean_$_yuqiyu1510455210493 started. 2017-11-12 10:53:52.999 INFO 3268 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: detected 1 failed or restarted instances. 2017-11-12 10:53:52.999 INFO 3268 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: Scanning for instance "yuqiyu1510454049066"'s failed in-progress jobs. 2017-11-12 10:53:53.003 INFO 3268 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: ......Freed 1 acquired trigger(s). 2017-11-12 10:54:00.020 INFO 3268 --- [ryBean_Worker-1] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 10:54:00 CST 2017復制代碼得到的結果是同樣可以完成任務的自動漂移。
如果兩個節點同時啟動,哪個節點先把節點信息注冊到數據庫就獲得了優先執行權。
傳遞參數到任務
我們平時在使用任務時,如果是針對性比較強的業務邏輯,肯定需要特定的參數來完成業務邏輯的實現。
下面我們來模擬商品秒殺的場景,當我們添加商品后自動添加一個商品提前五分鐘的秒殺提醒,為關注該商品的用戶發送提醒消息。
我們在節點quartz-cluster-node-first中添加秒殺提醒任務,如下所示:
在秒殺提醒任務邏輯中,我們通過獲取JobDetail的JobDataMap集合來獲取在創建任務的時候傳遞的參數集合,我們這里約定了goodId為商品的編號,在創建任務的時候傳遞到JobDataMap內,這樣quartz在執行該任務的時候就會自動將參數傳遞到任務邏輯中,我們也就可以通過JobDataMap獲取到對應的參數值。
設置秒殺提醒任務
我們找到節點項目quartz-cluster-node-first中的GoodInfoService,編寫方法buildGoodSecKillRemindTimer設置秒殺提醒任務,如下所示:
/*** 構建商品秒殺提醒定時任務* 設置五分鐘后執行* @throws Exception*/public void buildGoodSecKillRemindTimer(Long goodId) throws Exception{//任務名稱String name = UUID.randomUUID().toString();//任務所屬分組String group = GoodSecKillRemindTimer.class.getName();//秒殺開始時間long startTime = System.currentTimeMillis() + 1000 * 5 * 60;JobDetail jobDetail = JobBuilder.newJob(GoodSecKillRemindTimer.class).withIdentity(name,group).build();//設置任務傳遞商品編號參數jobDetail.getJobDataMap().put("goodId",goodId);//創建任務觸發器Trigger trigger = TriggerBuilder.newTrigger().withIdentity(name,group).startAt(new Date(startTime)).build();//將觸發器與任務綁定到調度器內scheduler.scheduleJob(jobDetail,trigger);}復制代碼我們模擬秒殺提醒時間是添加商品后的5分鐘,我們通過調用jobDetail實例的getJobDataMap方法就可以獲取該任務數據集合,直接調用put方法就可以進行設置指定key的值,該集合繼承了StringKeyDirtyFlagMap并且實現了Serializable序列化,因為需要將數據序列化到數據庫的qrtz_job_details表內。
修改保存商品方法,添加調用秒殺提醒任務:
添加測試商品
下面我們調用節點quartz-cluster-node-first的測試Chapter39ApplicationTests.addGood方法完成商品的添加,由于我們的quartz-cluster-node-second項目并沒有停止,所以我們在quartz-cluster-node-second項目的控制臺查看輸出內容:
2017-11-12 11:45:00.008 INFO 11652 --- [ryBean_Worker-5] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:45:00 CST 2017 2017-11-12 11:45:30.013 INFO 11652 --- [ryBean_Worker-6] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:45:30 CST 2017 2017-11-12 11:45:48.230 INFO 11652 --- [ryBean_Worker-7] c.hengyu.chapter39.timers.GoodAddTimer : 分布式節點quartz-cluster-node-second,商品添加完成后執行任務,任務時間:Sun Nov 12 11:45:48 CST 2017 2017-11-12 11:46:00.008 INFO 11652 --- [ryBean_Worker-8] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:46:00 CST 2017 2017-11-12 11:46:30.016 INFO 11652 --- [ryBean_Worker-9] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:46:30 CST 2017 2017-11-12 11:47:00.011 INFO 11652 --- [yBean_Worker-10] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:47:00 CST 2017 2017-11-12 11:47:30.028 INFO 11652 --- [ryBean_Worker-1] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:47:30 CST 2017 2017-11-12 11:48:00.014 INFO 11652 --- [ryBean_Worker-2] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:48:00 CST 2017 2017-11-12 11:48:30.013 INFO 11652 --- [ryBean_Worker-3] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:48:30 CST 2017 2017-11-12 11:49:00.010 INFO 11652 --- [ryBean_Worker-4] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:49:00 CST 2017 2017-11-12 11:49:30.028 INFO 11652 --- [ryBean_Worker-5] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:49:30 CST 2017 2017-11-12 11:49:48.290 INFO 11652 --- [ryBean_Worker-6] c.h.c.timers.GoodSecKillRemindTimer : 分布式節點quartz-cluster-node-second,開始處理秒殺商品:15,關注用戶推送消息. 2017-11-12 11:50:00.008 INFO 11652 --- [ryBean_Worker-7] c.h.c.timers.GoodStockCheckTimer : 分布式節點quartz-cluster-node-second,執行庫存檢查定時任務,執行時間:Sun Nov 12 11:50:00 CST 2017復制代碼秒殺任務在添加完成商品后的五分鐘開始執行的,而我們也正常的輸出了傳遞過去的goodId商品編號的參數,而秒殺提醒任務執行一次后也被自動釋放了。
總結
本章主要是結合上一章完成了分布式任務的講解,完成了測試持久化的定時任務自動漂移,以及如何向定時任務傳遞參數。當然在實際的開發過程中,任務創建是需要進行封裝的,目的也是為了減少一些冗余代碼以及方面后期統一維護定時任務。
本章源碼已經上傳到碼云:
SpringBoot配套源碼地址:gitee.com/hengboy/spr…
SpringCloud配套源碼地址:gitee.com/hengboy/spr…
SpringBoot相關系列文章請訪問:目錄:SpringBoot學習目錄
QueryDSL相關系列文章請訪問:QueryDSL通用查詢框架學習目錄
SpringDataJPA相關系列文章請訪問:目錄:SpringDataJPA學習目錄
SpringBoot相關文章請訪問:目錄:SpringBoot學習目錄,感謝閱讀!
歡迎加入QQ技術交流群,共同進步。
總結
以上是生活随笔為你收集整理的第四十章:基于SpringBoot Quartz完成定时任务分布式多节点负载持久化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装多个版本JDK相关问题
- 下一篇: 删除mongodb库