很多專案中都會用到定時任務的場景,起初專案中只是簡單的使用了spring提供的@Scheduled注解,隨著定時任務越來越多,我們需要對定時任務進行可視化管理,于是就單獨建立了一個工程,用quartz進行定時任務管理,
問題起因-無法暫停job
同事在除錯的時候發現通過介面添加的定時任務可以暫停、恢復,而原有的定時任務無法暫停,于是就引起我的好奇心,協助排查,
初步確認原因
先說明,由于我們目前使用的springboot版本為1.0,所以需要自行整合,到2.0時可以直接引入spring-boot-starter-quartz來實作自動裝載,具體可看QuartzAutoConfiguration自動配置類,
下面是暫停任務的方法:

表面上看并沒有什么特別問題,根據同事描述的場景我初步推測原有的任務與介面加入的任務應該不是由一個Scheduler管理,
下面分別是定時任務job中的Scheduler與介面中的Scheduler,很明顯不是一個Scheduler,A-job所屬A-Scheduler但是用B-Scheduler去暫停顯然不行,


回頭看來其實中資料庫中也能看出原因:

SCHED_NAME都不一樣,
為什么會出來兩個Scheduler
既然知道了是因為不同Scheduler導致的,那么為什么會出來兩個Scheduler?
首先先上配置類:

厲害的人可能一眼就看出問題,但是我水平有限還是花了比較多的時間才發現問題,
我是通過找到org.quartz.Scheduler介面的實作類org.quartz.impl.StdScheduler再找到他的創建工廠類org.quartz.impl.StdSchedulerFactory,因為要與資料庫做持久化所以實作類肯定是這個,閱讀StdSchedulerFactory,發現有個叫instantiate的初始化的方法,應該就是通過他來進行初始化,所以再方法上打個斷點進行除錯,

果然該方法進入兩次,通過除錯發現兩處入口,
- SchedulerFactoryBean的afterPropertiesSet方法,

SchedulerFactoryBean實作了InitializingBean介面,InitializingBean介面不陌生,就是為bean做初始化用的,

圖中480行就是創建Scheduler的地方,進入方法

可以看出他會再SchedulerRepository中先獲取,如果獲取不到就創建一個Scheduler,(PS:SchedulerRepository中可以或得到所有的Scheduler)通過除錯這里創建了一個叫schedulerFactoryBean的Scheduler,
- SchedulerFactoryBean的afterPropertiesSet方法,QuartzInitializerListener的contextInitialized方法

QuartzInitializerListener實作了ServletContextListener介面,該介面有兩個方法分別contextInitialized與contextDestroyed分別是Servlet容器初始化及銷毀時執行,
查看contextInitialized方法:


可以看到里面也創建了一個Scheduler,通過除錯知道該Scheduler名字為QuartzScheduler(默認名)
問題原因
所以為什么會出來兩個Scheduler,是因為配置了兩個,

這里我選擇去掉QuartzInitializerListener,
調整后的配置:

與配置中心Apollo結合
因為線上線下資料庫配置不一樣,每次部署時都要替換quartz.properties檔案非常麻煩,之前嘗試過與apollo整合,當發現啟動時還是會用到quartz.properties檔案,一直找不到原因,通過上文排查也隨便解決了這個問題是因為QuartzInitializerListener這里默認用到了,而schedulerFactoryBean說用到配置可以指定,
將資料庫相關資訊方入Apollo,通過@Value注解獲取即可,

ps:同事也是網上找篇文章然后照搬下來,網上的文章水平參差不齊,照搬時還是要貨比三家要有自己的判斷,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/247043.html
標籤:Java
