在重構(gòu)一個老項目的一個定時任務服務的過程中,我想到了幾個有趣的點子,整個服務的骨架就是借鑒這幾個點子搭建的。
創(chuàng)新互聯(lián)專注于福安企業(yè)網(wǎng)站建設,成都響應式網(wǎng)站建設公司,商城網(wǎng)站建設。福安網(wǎng)站建設公司,為福安等地區(qū)提供建站服務。全流程按需網(wǎng)站策劃,專業(yè)設計,全程項目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務一開始想做的,只是能讓定時任務實現(xiàn)可頁面配置,可隨時修改配置隨時生效。配置指的是配置cron表達式,定義任務的執(zhí)行時機。但由于后期的種種問題,不得不對定時任務服務進行再次改造,所以,定時任務服務經(jīng)歷了三個階段。
第一個階段:
目的:定時任務做成可配置。
缺點:發(fā)現(xiàn)定時任務都很耗內(nèi)存,且由于執(zhí)行時間過長,通常幾分鐘的都有,這樣就會有任務碰撞到一起執(zhí)行的情況,至少CPU長期百分百使用狀態(tài)。比如報表統(tǒng)計類任務,大多定義在每個小時的前10分鐘內(nèi)完成。
第二個階段:
目的:減少內(nèi)存和降低CPU的使用率。
方案:將定時任務串行化執(zhí)行,由一個單一線程的線程池去執(zhí)行。
缺點:將任務串行化執(zhí)行后,會有風險。比如因某個卡住了,導致后面的任務都得不到執(zhí)行。
第三階段:
目的:解決串行化執(zhí)行的弊端。
方案:引入監(jiān)視器。如果有任務從提交到執(zhí)行,時間超過15分鐘還未完成,就直接中斷線程,讓下個任務能夠得以執(zhí)行,并發(fā)送郵件通知便于排查原因。
之前學匯編的時候知道操作系統(tǒng)有個引導器的存在,就是在系統(tǒng)盤的某個開始位置,由主板上的程序加載執(zhí)行,系統(tǒng)再由引導器啟動。定時任務也應該有一個啟動器來初始化配置并提交到調(diào)度線程池,所以我借鑒了系統(tǒng)引導器的設計。
要求所有定時任務都實現(xiàn)定時任務接口TimeTaskPlayer。因為調(diào)度線程池要求submit是一個Runnable,所以定時任務接口要繼承Runnable接口,由 run 方法調(diào)用子類實現(xiàn)的startPlayer方法。至于為什么不讓子類(定時任務)直接實現(xiàn)run方法,后面會有用處。
定義引導器接口
實現(xiàn)定時任務啟動引導器
在Spring boot初始完成后,調(diào)用引導器初始化服務
當然,優(yōu)雅退出肯定也不能少呀,其實可以直接使用spring的優(yōu)雅退出的,都是使用的同一個原理,注冊jvm鉤子。
提供一個所有任務的ScheduleFuture的持有者,提供停止所有任務的方法,用于更新配置后取消所有定時任務,由引導器重新啟動。即更新配置后重啟所有定時任務。
任務的Cron表達式配置管理類,提供reloadCronFromDB方法給接口調(diào)用更新任務的cron表達式緩存。這里的注釋有改動,存的不是完整類名,而且去掉包名后的類名,同時Bean的name(spring管理)也是去掉包名后的類名,首字母大寫。
三個方法很好理解,一個是根據(jù)定時任務的Class獲取cron表達式,如果緩存沒有,則從數(shù)據(jù)庫加載。第二個是獲取定時任務的狀態(tài),用于控制是否啟用這個定時任務。
當然,還有使用Aop添加任務執(zhí)行異常郵件通知,這里就不貼了。
如何將定時任務控制串行執(zhí)行,且不改動現(xiàn)有代碼呢,如果改動太大就相當于重構(gòu)了。這時候我想到了插件。插件我們常常用到,比如idea就有很多插件,再與我們貼近點的就是Mybatis的分頁插件。插件,無外呼就是在某些任務開始之前插入埋點代碼,其實也是AOP編程思想。所以我借鑒了插件這一思想,來實現(xiàn)不修改現(xiàn)有代碼的情況下將定時任務串行執(zhí)行。這里使用了觀察者模式。
觀察者模式:抽象觀察者
觀察者模式:抽象主題
觀察者模式:具體的定時任務事件執(zhí)行者,即觀察者。這里包含了監(jiān)聽器的內(nèi)容,就是將事件轉(zhuǎn)為任務放入單線程的線程池后,拿到Future,交給監(jiān)聽器監(jiān)控任務的執(zhí)行狀態(tài)。
觀察者模式:具體的事件主題,接收事件并通知對該事件感興趣的觀察者。
那么,何時發(fā)布的事件呢?就是定時任務到執(zhí)行時間的時候。文章開頭就埋下了一個點,就是定時任務接口TimedTaskPlayer為何不讓子類直接實現(xiàn)run方法,為的就是可以在不改任務代碼的情況下,實現(xiàn)讓定時任務改為串行執(zhí)行。
修改后的TimedTaskPlayer接口如下圖,注意看run方法,神不知鬼不覺的就能將任務的執(zhí)行權(quán)轉(zhuǎn)交出去。定時任務就只是一個任務的執(zhí)行時間節(jié)點的掌控者,不再是任務執(zhí)行的掌控者,簡簡單單的就被抽空了身體。
如何杜絕串行任務因單個任務阻塞導致服務崩潰呢?當我們使用idea編碼的時候,因打開的軟件太多,就會導致系統(tǒng)變卡,但是我們可以通過系統(tǒng)進程監(jiān)視器看到idea卡住了,我們可以選擇手動殺掉重啟。
所以,我想我的定時任務系統(tǒng)也能有這樣的功能。加入監(jiān)視器,在任務提交到單線程線程池時,也將返回的Future提交到監(jiān)視隊列,由監(jiān)視器線程輪詢隊列中任務的執(zhí)行情況,發(fā)現(xiàn)超時未執(zhí)行完的任務直接中斷執(zhí)行,否則將任務放入監(jiān)視隊列末尾。這里的超時目前我只能拿任務的提交時間和當前時間計算。
定時任務模塊中還有一個消息訂閱消費的小模塊,當然這與定時任務沒有關系。這里我用到了一種設置模式,叫條件執(zhí)行器。啥?正如過濾器與攔截器是責任鏈的一種變種一樣,條件執(zhí)行器也是策略模式的一種變種,當然條件執(zhí)行器是我亂叫的。
為啥叫條件執(zhí)行器,在使用switch分支語句的時候,我們可以定義case1、2、3執(zhí)行某個邏輯,case4執(zhí)行某個邏輯。一樣的,一條消息可能會有很多條件執(zhí)行器感興趣,也可能沒有任何條件執(zhí)行器感興趣,也可能只有一個條件執(zhí)行器感興趣。與switch很像,所以我叫它條件執(zhí)行器。當然,這類消息屬于通知類消息,無論消費成功或失敗,都不會再有第二次消費。
定時任務串行化執(zhí)行有風險,但卻是為了能在4g內(nèi)存的機器上跑起來。但是,如果出現(xiàn)有任務把線程堵住的情況,那就是代碼有問題,如果是代碼的問題,即便是多線程,風險一樣存在,甚至更高。為何這個說,假如一個任務3分鐘執(zhí)行一次,結(jié)果每次都把線程堵住,要么把內(nèi)存玩爆,要么把線程池隊列阻塞滿,最后還不是一樣的下場。
當然,并非所有業(yè)務場景都適用,如果對定時任務要求及時的,就不能這么用,比如我一定要讓這個任務0點0分執(zhí)行。或者當任務越來越多的時候,比如有上百個,上百個任務串行執(zhí)行想下什么后果。
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
標題名稱:我重構(gòu)定時任務服務時,運用的那些編程思想-創(chuàng)新互聯(lián)
URL地址:http://aaarwkj.com/article38/jecpp.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供域名注冊、網(wǎng)站營銷、品牌網(wǎng)站設計、網(wǎng)站改版、動態(tài)網(wǎng)站、網(wǎng)站建設
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容