1. 程式人生 > >定時任務莫名停止,Spring 定時任務存在 Bug??

定時任務莫名停止,Spring 定時任務存在 Bug??

Hello~各位讀者新年好!這裡樓下小黑哥給大家拜個年,祝大家蒸蒸日上燙燙燙,年年有餘屯屯屯。

那年那 Bug

春節放假,小黑哥坐上高鐵回家,突然想到一次生產問題。那是小黑哥參加工作第一年,那一年國慶假期,小黑哥提前一天請假回家辦個護照。那時候剛開始負責一個生產系統,所以工作日請假,還是有點擔心,就怕問題看小黑哥不在,悄然上門。

哎,真實越怕什麼,就來什麼。

高鐵開到一半的時候,同事反饋系統不能獲取最新的流水資訊(流水資訊通過 Spring 定時任務定時拉取)。小黑哥心裡一驚,立刻拔出電腦,連上 VPN,準備登上生產機器,檢視系統情況。可是,高鐵上網路大家也懂,很不穩定,連了好久連不上 VPN,只好遠端指揮同事看一下系統日誌。通過同事反饋的日誌,發現拉取流水定時任務沒有執行,進一步檢視,小黑哥發現整個系統其他的定時任務也都停止了。。。

這真是一個奇怪的的問題,這好端端的定時任務怎麼會突然停止?

暫時想不到解決辦法,只好指揮同事先重啟應用。重啟之後,暫時解決問題,定時任務重新開始執行,也獲取到最新的付款流水資訊。

問題排查

到家之後,小黑哥立刻登上生產機器,檢視系統日誌,發現重啟之前某一定時任務執行到一半,並且在這之後其他定時任務就沒有再被執行。

通過系統日誌,定位到了有問題的程式碼。

這裡採用重試補償策略,防止查詢流水資訊因為網路等問題發生偶發的失敗。這個策略面對偶發的失敗沒什麼問題,但是如果查詢銀行流水服務一直失敗,這段程式碼就會陷入死迴圈。恰巧那段時間網路出現一些問題,導致這裡查詢一直處於失敗。

增加最大重試次數,修復該 Bug

修復之後,立刻將最新版本程式碼部署到生產系統,暫時解決了這個問題。

知識點:面對一些失敗,可以採用重試補償策略,重新執行,最大可能保證執行成功,但是這裡切記設定合適的的重大的次數。

深入排查

雖然問題解決了,但是小黑哥心裡還是存在一個疑惑,為何一個定時任務發生了阻塞,就會影響執行其他定時任務。小黑哥最初的理解是不同的定時任務應該互相隔離,互不影響才對,真難到是 Spring 定時任務的 Bug 嗎?

想到這裡,小黑哥決定寫一個 Demo,復現問題,然後深入原始碼排查。

啟動程式,日誌輸出如下:

從日誌可以看到,fixDelayMethod 方法執行之後進入休眠,直到休眠結束,cronMethod

定時任務才有機會被執行。另外從上面可以看到,上述兩個定時任務都由 pool-1-thread-1執行緒執行。從這點可以看出 Spring 定時任務將會交給執行緒池執行。

知識點: 執行緒池中執行緒預設命名策略為 pool-%poolNumber-thread-%num。

如果執行緒池只有一個工作執行緒,該執行緒一旦被長時間阻塞,堆積的其他任務就沒有機會被執行。

那麼是不是這個問題導致的 Sping 定時任務停止執行?我們繼續往下排查。

圖上日誌綠色部分, ScheduledAnnotationBeanPostProcessor 輸出一個重要資訊:

No TaskScheduler/ScheduledExecutorService bean found for scheduled processing

檢視 Spring 文件,Spring 內部將會通過呼叫 TaskScheduler 執行定時任務,而另一個 ScheduledExecutorServiceJDK 提供執行定時任務的執行器。記住這兩者

通過這段日誌,使用 IDEA 的強大的關鍵字搜尋功能,定位到 ScheduledAnnotationBeanPostProcessor#finishRegistration 方法。

這個方法比較長,大家重點關注圖中標示的幾處。

Spring 啟動之後將會掃描所有 Bean 中帶有 @Scheduled 註解的方法,然後封裝成 Task 子類放置到 ScheduledTaskRegistrar

這段程式碼位於 ScheduledAnnotationBeanPostProcessor#processScheduled,感興趣的可以翻閱檢視

如果此時 ScheduledTaskRegistrar 不存在定時任務或者 ScheduledTaskRegistrar 中的 TaskScheduler不存在,finishRegistration將會多次呼叫 ScheduledAnnotationBeanPostProcessor#resolveSchedulerBean 方法用以查詢 TaskScheduler/ScheduledExecutorService

接下去將會把獲取到 Bean 通過 setScheduler 注入到 ScheduledTaskRegistrar 中。

如果獲取的為 ScheduledExecutorService 型別,將會將其封裝到 taskScheduler中。

最後還沒找到,將會輸出最剛開始見到的日誌。然後 Spirng 將會在 ScheduledTaskRegistrar#afterPropertiesSet 建立一個單執行緒的定時任務執行器 ScheduledExecutorService,注入到 ConcurrentTaskScheduler中,然後通過 taskScheduler 執行定時任務。

交給TaskScheduler 的定時任務最後實際上還是通過 ScheduledExecutorService執行。

這裡可以得出一個結論:

Spring 定時任務實際上通過 JDK 提供的 ScheduledExecutorService執行。預設情況下,Spring 將會生成一個單執行緒ScheduledExecutorService執行定時任務。所以一旦某一個定時任務長時間阻塞這個執行執行緒,其他定時任務都將被影響,沒有機會被執行執行緒執行。

Spring 這種預設配置,在需要執行多個定時任務的情況,可能會是一個坑。我們可以通過改變配置,使 Spring 採用多執行緒執行定時任務。

自定義配置

Spring 可以通過多種方式改變預設配置。

xml 配置

通過 xml 配置 TaskScheduler 執行緒數。

<task:annotation-driven  scheduler="myScheduler"/>
<task:scheduler id="myScheduler" pool-size="10"/>

通過上面的配置,Spring 將會使用 TaskScheduler 子類 ThreadPoolTaskScheduler,內部執行緒數為 pool-size 數量,這個執行緒數將會直接設定 ScheduledExecutorService 執行緒數量。

註解配置

在上面問題排查中,我們知道 Spring 將會查詢 TaskScheduler/ScheduledExecutorService,若存在將會使用。所以這裡我們可以生成這些類的 Bean

以上方式二選一即可

SpringBoot 配置

上面兩種配置適用於普通 Spring,比較繁瑣。相比而言 SpringBoot 配置將會非常簡單,只需要在啟動配置檔案加入如下配置即可。

spring.task.scheduling.pool.size=10
spring.task.scheduling.thread-name-prefix=task-test

技術總結

下面開始技術總結:

  1. Spring 定時任務執行原理實際使用的是 JDK 自帶的 ScheduledExecutorService
  2. Spring 預設配置下,將會使用具有單執行緒的 ScheduledExecutorService
  3. 單執行緒執行定時任務,如果某一個定時任務執行時間較長,將會影響其他定時任務執行
  4. 如果存在多個定時任務,為了保證定時任務執行時間的準確性,可以修改預設配置,使其使用多執行緒執行定時任務
  5. 面對偶發的失敗,我們可以採用重試補償策略,不過這裡切記設定合適的最大重試次數

隨便聊聊

對於常用的開源框架,我們不僅要掌握怎麼用,還要熟悉相關的配置,最後還應該去了解其內部的使用的原理。這樣出了問題,我們也能很快定位問題,找到問題的實際原因。

幫助文件

Spring scheduling-task-scheduler

歡迎關注我的公眾號:程式通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的部落格:studyidea.cn

相關推薦

定時任務莫名停止Spring 定時任務存在 Bug??

Hello~各位讀者新年好!這裡樓下小黑哥給大家拜個年,祝大家蒸蒸日上燙燙燙,年年有餘屯屯屯。 那年那 Bug 春節放假,小黑哥坐上高鐵回家,突然想到一次生產問題。那是小黑哥參加工作第一年,那一年國慶假期,小黑哥提前一天請假回家辦個護照。那時候剛開始負責一個生產系統,所以工作日請假,還是有點擔心,就怕問題看小

spring定時器使用註解@Scheduled執行任務fixedDelay、fixedRate和cron區別

 註解@Scheduled 可以作為一個觸發源新增到一個方法中,例如,以下的方法將以一個固定延遲時間5秒鐘呼叫一次執行,這個週期是以上一個呼叫任務的 完成時間 為基準,在上一個任務完成之後,5s後再次執行:

Quartz cron 表示式(linux 定時java 定時任務spring task定時任務)

原文地址:https://blog.csdn.net/feng27156/article/details/39293403 Quartz cron 表示式的格式十分類似於 UNIX cron 格式,但還是有少許明顯的區別。區別之一就是 Quartz 的格式向下支援到秒級別的計劃,而 UNIX cron 計劃

spring定時任務實現動態定時任務(啟停週期修改)

使用方法:             複製下方程式碼,在業務需要處呼叫(定時任務狀態和執行週期被修改後),一定要在專案啟動時後立刻執行一次庫中全資料呼叫此方法,List<Cron> crons   Cron中一定要有業務類的包加類名(com.rails.trave

Spring定時任務開發實現動態修改時間引數手動開啟關停等

@Service @Lazy(false) @EnableScheduling public class MyScheduler extends BaseService implements SchedulingConfigurer{ public static String cron

關於Java Web 使用Spring中使用Quartz(定時呼叫、實現固定時間執行) 觸發定時器(執行某些任務)的例項

第一步:pom.xml中Maven下載需要的jar架包。 <!--```````````定時器```````````--> <dependency> <grou

動態改變Cron表示式來實現動態執行Spring定時任務

 spring定時器非常強大,但是有時候我們需要在不需要重啟應用就可以動態的改變Cron表示式的值。這樣我們可以在配置檔案中寫一個表示式的值。每次想改變表示式的值的時候手動修改配置檔案中的表示式,實現動態表示式。但是我們也可以從資料庫中動態讀取,都是一樣的。 一:首先定

httpclient導致的spring schedule定時任務全部停止問題

前兩天vos線上服務遇到一個問題,定時任務突然全部停下來不跑了。看日誌也沒發現什麼明顯的異常輸出,加上比較忙,所以乾脆直接使用重啟大法。沒想到過了兩天突然發現定時任務又全部停了,恰好正在趕需求,vos又是一個公司內部使用的系統,優先順序較低,索性繼續重啟等有空了

Spring定時任務Spring4整合quartz2.2quartz-scheduler定時任務

一、首先加入spring(4.1.9.RELEASE)的依賴包,然後再加入quartz(2.2.1)的包,如下: Xml程式碼   <dependency>     <groupId>org.quartz-scheduler</gr

Spring定時任務的幾種實現

job 將不 obb 簡單 信息 sim ger 觸發 一個 Spring定時任務的幾種實現: 近日項目開發中需要執行一些定時任務,比如需要在每天淩晨時候,分析一次前一天的日誌信息,借此機會整理了一下定時任務的幾種實現方式,由於項目采用spring框架,所以我都將結合 sp

​老男孩教育每日一題-第85天-下面這個腳本直接執行沒有問題定時任務中有問題什麽原因?

定時任務 環境變量問題 無法取出ip地址 每日一題 腳本內容:[[email protected]/* */ scripts]# cat /server/scripts/ip.sh #!/bin/bash IP=$(ifconfig eth0 |awk -F "[ :]+" ‘N

Spring定時任務的幾種實現 (記錄備用)

官方 ini src def lns 開發 enc http poj http://gong1208.iteye.com/blog/1773177 Spring定時任務的幾種實現 近日項目開發中需要執行一些定時任務,比如需要在每天淩晨時候,分析一次前一天的日誌信息,借此

spring定時任務

strac 屬性 ase port tin aop init 都是 開發 轉自:http://gong1208.iteye.com/blog/1773177 近日項目開發中需要執行一些定時任務,比如需要在每天淩晨時候,分析一次前一天的日誌信息,借此機會整理了一下定時任務

spring定時任務執行兩次的原因與解決方法

ref net 任務 article 服務 每次 bsp tail 本地 spring定時任務,本地執行一次,放到服務器上後,每次執行時會執行兩次,原因及解決辦法。 http://blog.csdn.net/yaobengen/article/details/70312

Spring 定時任務之 @Scheduled cron表達式

按順序 cron sun ron cell last div text table 一個cron表達式有至少6個(也可能7個)有空格分隔的時間元素。 按順序依次為 秒(0~59) 分鐘(0~59) 小時(0~23) 天(月)(0~31,但是你需要考慮你月的天數) 月(0~

spring定時任務的註解實現方式

sched class 單線程 pool job 默認 clas 線程 ref STEP 1:在spring配置文件中添加相應配置,以支持定時任務的註解實現 (一)在xml裏加入task的命名空間 <!-- beans裏添加:--> xmlns:task=

spring 定時任務

表示 eth ask color ring family font bean pre <!-- 定時任務 --><task:annotation-driven/> <!-- 註入定時任務 --><bean id="TimerTask

shell正常運行加入定時任務執行失敗

執行 code 命令 ifconfig post 定時 正常 pos ont 例如簡單的ifconfig命令,在shell中運行成功,但是在crontab 中執行失敗。 定位原因:環境變量 解決方案: whereis ifconfig 然後在shell中加入: PATH

linux服務器上使用crontab指令執行制定PHP文件生成定時任務

php sage 對象 安裝 dbn 個人 mage message into 首先 寫個 PHP文件 ,我寫的是向數據庫表插入數據,如下圖 <?php $dbms=‘mysql‘; //數據庫類型 $host=‘localhost‘; //數據庫主機名 $dbN

Spring 定時任務Scheduled 開發詳細圖文

定時執行 本地 setting 工具類 location fir clean scan crontab Spring 定時任務Scheduled 開發 文章目錄 一、前言 1.1 定時任務 1.2 開發環境 1.3 技術實現 二、創建包含WEB.