由于一些代碼遺留問(wèn)題,決定重構(gòu)定時(shí)任務(wù),中間出了一些問(wèn)題,希望能夠幫助到大家。
定時(shí)任務(wù)
業(yè)務(wù)中經(jīng)常會(huì)有一些需要定時(shí)任務(wù)或者任務(wù)調(diào)度系統(tǒng)的場(chǎng)景,例如:數(shù)據(jù)備份,訂單超時(shí),數(shù)據(jù)統(tǒng)計(jì)以及一些特殊業(yè)務(wù)需求。往往這也是最容易出問(wèn)題的地方。說(shuō)一下代碼重構(gòu)的時(shí)候?qū)Χ〞r(shí)任務(wù)的一些見解,歡迎大家批評(píng)指正!
下面說(shuō)的單機(jī)或是一臺(tái)物理機(jī)或者是跨進(jìn)程的應(yīng)用。
- 分布式下使用分布式鎖
大多數(shù)情況下,服務(wù)都是多機(jī)部署的,對(duì)于數(shù)據(jù)有并發(fā)限制的情況,一定要使用分布式鎖,確保任務(wù)是在單機(jī)運(yùn)行的。 - 禁止單任務(wù)并發(fā)進(jìn)行,多任務(wù)可并行
這條適用的情況和分布式鎖大概相同,目前定時(shí)任務(wù)調(diào)度框架多是默認(rèn)并發(fā)的,可能相同的任務(wù)同時(shí)進(jìn)行,但是任務(wù)內(nèi)部又未做并發(fā)處理,會(huì)造成最終數(shù)據(jù)的不準(zhǔn)確;對(duì)于不同類型且不產(chǎn)生關(guān)聯(lián)的任務(wù)可以采用并行處理。 - 使用quartz而非Spring Task
Task雖然更加輕量化,但是很多高級(jí)特性并不支持,同時(shí)對(duì)于任務(wù)失敗的情況并不會(huì)進(jìn)行處理,不能確保任務(wù)不可控中斷時(shí)會(huì)自動(dòng)重啟任務(wù)。 - 任務(wù)內(nèi)部多線程的使用
這里不討論并發(fā)的處理,任務(wù)中一些不需要同步或者耗時(shí)的操作交由線程池或者forkjoin執(zhí)行的時(shí)候,執(zhí)行結(jié)果應(yīng)該與schedule線程同步,這里說(shuō)的同步并非嚴(yán)格意義上的線程同步,舉個(gè)例子說(shuō),在分布式環(huán)境中,如果任務(wù)必須需要獲取分布式鎖來(lái)保證單機(jī)執(zhí)行,任務(wù)可以交由線程池異步處理,定時(shí)器線程可以處理一些其他的事情,但是定時(shí)器線程必須等待異步處理的結(jié)果,來(lái)決定是否釋放分布式鎖,或者采用守護(hù)線程監(jiān)聽的方式來(lái)決定鎖的狀態(tài),因?yàn)橐坏╂i釋放,極其有可能被別的機(jī)器獲取,而此時(shí)本機(jī)還在執(zhí)行任務(wù),從而導(dǎo)致并發(fā)問(wèn)題。使用線程池僅僅是為了加快任務(wù)處理速度或者不阻塞一些輔助操作,線程池要使用自定義拋棄策略和異常策略,不允許線程池向外拋出異常,被schedule線程捕獲之后,即使schedule線程擁有處理能力,也有可能會(huì)導(dǎo)致定時(shí)任務(wù)提前結(jié)束。 - 執(zhí)行粒度與容錯(cuò)性
舉個(gè)簡(jiǎn)單的例子,一家十個(gè)部門,每個(gè)部門有100個(gè)人,每個(gè)月10號(hào)要定時(shí)發(fā)工資,有很多種處理的方式,可以對(duì)一個(gè)部門的100人批處理發(fā)工資,也可以一個(gè)人一個(gè)人的發(fā),可能一批人同時(shí)發(fā)因?yàn)橐粋€(gè)人有問(wèn)題導(dǎo)致所有人都發(fā)不了,對(duì)于這種上下級(jí)數(shù)據(jù)以及同級(jí)數(shù)據(jù)間無(wú)關(guān)聯(lián)性的情況,下級(jí)異常不能影響上級(jí) 同級(jí)異常不影響后續(xù),即一個(gè)人出問(wèn)題,不能影響別人,更不能影響部門。 - 補(bǔ)救
定時(shí)任務(wù)處理失敗的情況經(jīng)常會(huì)發(fā)生,但是對(duì)于能考慮到異常的情況要制定一定的補(bǔ)救措施,能夠自動(dòng)恢復(fù),也可以增加人工補(bǔ)救的措施。