背景和目的
? ? ? 在日常的開發(fā)中,我們經(jīng)常會遇到各種異步的處理,業(yè)務(wù)的補償和定時的業(yè)務(wù)處理。這個時候,我們可能第一反應(yīng)就是,起一個定時任務(wù)。沒錯,就是定時任務(wù)。
? ? ? ?目前所在的這家公司,由于交易系統(tǒng)是在我職責(zé)范圍之內(nèi),而交易系統(tǒng)中很多業(yè)務(wù)都是通過異步定時來處理的。之前發(fā)生了好幾次由于定時任務(wù)觸發(fā)異常導(dǎo)致的種種生產(chǎn)問題,不得不使我停下忙碌的腳步,靜下來思索并總結(jié)。此文屬個人經(jīng)驗總結(jié),如有雷同,純屬巧合。好,那我們開始入正題。
定時任務(wù)的演變
“演變”可能說的有點嚴重。還記得我07年剛畢業(yè)的時候,在第一公司,那個時候印象還沒接觸定時任務(wù),隱約在自己買的一本spring的書中,了解到spring-quartz。后來到了第二家公司,當時還是平安集團信息管理中心(后獨立成平安科技),接觸到了spring-quartz的應(yīng)用場景。到現(xiàn)在,我們互聯(lián)網(wǎng)技術(shù)不斷發(fā)展,分布式任務(wù)調(diào)度層出不窮。基本可以概括如下:
spring-quartz階段:
早期的方式一般采用quartz結(jié)合spring來實現(xiàn)集群中的定時任務(wù)的啟動。有興趣的朋友可以看看quartz的12張表的作用。大家一直認為quartz就是屬于spring,其實是一個誤解。只是我們當時的框架是ssh而已。
這個階段,更多的是保險、金融、銀行會用的比較多,做一些企業(yè)級應(yīng)用。而企業(yè)級應(yīng)用一般是集群部署。集群部署需要考慮的是定時任務(wù)的重復(fù)跑問題。quartz在這一點做的還是挺到位的。只是配置比較復(fù)雜。我還清晰的記得,當時在熟悉quartz的時候,那些oracle數(shù)據(jù)庫的表,讓我很頭疼。看了很久才明白。其實他的思想還是挺不錯的。基本是以節(jié)點任務(wù)搶占的方式來實現(xiàn)。用戶數(shù)據(jù)庫悲觀鎖的方式來實現(xiàn)共享資源的搶占。
山寨版實現(xiàn):Thread.sleep
這個相對來說就比較簡單了。用while循環(huán),循環(huán)中用sleep來處理,當然,這種實現(xiàn),最好用線程來處理,異步的啟動。否則會阻塞應(yīng)用。
spring注解Scheduler
spring的scheduler,其實是一種集成框架,支持JDK Timer、concurrent、quartz三種,這三種任務(wù)調(diào)度方案在實現(xiàn)機制和調(diào)用方法上都不同,但spring通過對其包裝,使得基于spring能用統(tǒng)一的配置和編碼風(fēng)格來使用這三種schedule方案。
jdk的timer實現(xiàn)
jdk推陳出新,在1.3的時候推出了timer。主要包括任務(wù) TimerTask 、任務(wù)隊列:TaskQueue queue和任務(wù)調(diào)試。具體大家自行研究下。一段時間風(fēng)靡。
但是官網(wǎng)上有這樣子的一段話:
Timer類會自動啟動一個新線程,而多個Timer類則會有開辟多個線程,同時Timer類的線程是非daemon(守護)線程,所以一旦啟動除非明確cancel掉,是一直存在的。
因此,很容易出現(xiàn)oom。
分布式任務(wù)調(diào)度框架
隨著電商互聯(lián)網(wǎng)時代的到來,追求效率的攻城獅們,需要做分布式任務(wù)處理。不再滿足于一臺機器處理一個任務(wù),而需要多臺機器處理一個任務(wù)。用空間來換時間。
分布式任務(wù)調(diào)度框架(輪子)產(chǎn)生了。我清晰的記得,當時在一號店,研究了下淘寶的tbscheduler,而且還改寫了部分源碼的實現(xiàn)。現(xiàn)在想想,當時還做了部分的無用功,但是一定程度上加深了我對整體框架的認識和原理的熟悉。從此一發(fā)不可收拾的走上了架構(gòu)這條路。
基本當前階段主流的任務(wù)調(diào)度框架(我所說的可能是基于我的認知)如tbscheduler、es-job。原來還是基于spring進行實現(xiàn)。當然,不排除現(xiàn)在各家公司自己造輪子。開源出來的比較有名的就是這些了。es-job其實和tbscheduler原理一樣。當初看到這個開源框架的時候,甚至懷疑是抄襲tbscheduler的。畢竟在11年的時候,就開始讀tbscheduler的源碼了。
以上,基本能概括目前的主流定時任務(wù)實現(xiàn)。
任務(wù)的部署方式
看了以上的演變過程,我想應(yīng)該已經(jīng)喚醒大家沉睡多年的記憶了^_^。
集群時代
時間回到07年左右,那個時候,保險金融業(yè)大多采用了小型機+pc server+oracle+ssh的架構(gòu),混搭。滿天飛的存儲過程,數(shù)據(jù)同步是etl和golden-gate。土豪的時代。
言歸正傳,weblogic的時代,采用集群方式部署。必然會考慮定時任務(wù)的啟動問題,數(shù)據(jù)的重復(fù)跑問題,而quartz正好能解決規(guī)律。所以,那個時候,定時任務(wù)隨著應(yīng)用一起部署。
通過數(shù)據(jù)庫的鎖方式,規(guī)避了任務(wù)的重復(fù)自行,這個時候,任務(wù)是隨機搶占,無狀態(tài)。記錄日志,和任務(wù)的批次執(zhí)行結(jié)果,配合監(jiān)控報警。基本完美解決了問題。
缺點很明顯:實現(xiàn)復(fù)雜。我想,那12張表,就已經(jīng)很折騰人了。
單機時代
隨著電商、互聯(lián)網(wǎng)的興起,效率和速度,成了大家首要追求的目標。“天下武功,唯快不破”。這個時代,有個口號:去IOE。
去IOE,有幾個原因:
1、成本。之所以放首要,是因為電商很多燒錢,成本放在第一位。印象中oracle的licence一個每年都是十幾萬,golden-gate跟著licence也是靠近10w。一般數(shù)據(jù)庫都是集群部署,那么單db這塊,每年就很多錢。更不用說小型機和EMC的存儲設(shè)備了。
2、效率和性能。電商的場景和傳統(tǒng)的場景有很大的差別,對于并發(fā)的要求、對于中間件的性能有很多的考量。而付費產(chǎn)品,很多不開源,且有使用限制,制約了平臺的擴展性。因此,很多公司喜歡自己造輪子。這里最典型的就是bat中的A。
以上,是我理解的兩個主要原因。可能還有其他的,我也不過多的去贅述了。
另外,除了以上,還有一些點很重要,我覺得有必要提下。那就是電商和互聯(lián)網(wǎng)是不建議存儲過程以及觸發(fā)器和函數(shù)等操作的。也是基于資源和性能的考慮。
還記得一號店曾經(jīng)進行過去存儲過程、trigger和函數(shù)的行動。原因就是數(shù)據(jù)庫資源太寶貴了,相比應(yīng)用的優(yōu)化空間,真的有限。而應(yīng)用層面,有很多優(yōu)化的措施,如緩存,水平擴容等等。且db在一定程度上是共享資源,小范圍說,會影響某個業(yè)務(wù)域的集群,大了說,會影響整個公司的業(yè)務(wù)開展。
回到正題,這個時候,我們推崇的是簡便,快捷,性能。所以定時任務(wù)單獨部署,和應(yīng)用解耦。
優(yōu)點:
顯而易見,和應(yīng)用分離,任務(wù)掛或者發(fā)生死鎖,不影響業(yè)務(wù)。
缺點:
單獨部署,沒有實現(xiàn)集群方式。任務(wù)一般作為補償,非主鏈路手段。且有消息保證。(當然,也可以剝離開來進行集群,但是那個時候沒想這么復(fù)雜),更多的是分離。
分布式時代
先上個圖:
屌絲的福音。
因為再也不用去考慮任務(wù)的搶占,任務(wù)的效率問題了。分布式任務(wù)框架能很好的解決這些問題。
我這里總結(jié)下框架出現(xiàn)的原因:
1、效率:原來是并發(fā)執(zhí)行,也就是一個cpu處理多個事件。這個時候,我們發(fā)現(xiàn),其實資源沒有被充分利用起來。可以進行并行執(zhí)行。多核并行。我們想充分利用資源,提升執(zhí)行效率。用空間來換事件。
2、規(guī)范:這里的規(guī)范是任務(wù)的實現(xiàn)規(guī)范。我想大家更多的是考慮的第一點。但是我當時在一號店去引入tbscheduler的動因,還有一個,就是書寫的規(guī)范。但是各種任務(wù)書寫形式,層出不窮,以上列舉的可能都有出現(xiàn)。拍拍貸習(xí)慣叫收口,那么我姑且借用下,叫“收口”任務(wù)的實現(xiàn)方式。
分布式任務(wù)框架的出現(xiàn),在一定程度上解決了如下問題:
1、部署的問題。
2、資源使用的問題。
3、任務(wù)分片的問題。
如下一一解釋:
1、部署問題:原先的部署,涉及到數(shù)據(jù)庫,應(yīng)用。且要考慮任務(wù)的搶占。low之余多了一點復(fù)雜。現(xiàn)在框架集群部署,甚至支持docker。任務(wù)的注冊采用zookeeper或者consul等開源框架,任務(wù)的調(diào)度由sdk植入應(yīng)用。簡單明了。
2、資源使用問題:
早期的定時任務(wù),要么就一臺機器使用,要么就輪著來。簡單粗暴。但是資源使用率不高。
如今,手槍換大炮。可以給機器設(shè)置權(quán)重,資源多的機器,多執(zhí)行任務(wù)。資源少的,執(zhí)行一個或者跳過。
3、任務(wù)分片問題:
這個是分布式的理念,用空間換時間。我不做過多的贅述。
結(jié)合業(yè)務(wù)
相信以上說了這么多,大家塵封已久的記憶已經(jīng)被徹底喚醒。那么,接下來,我將要結(jié)合具體的業(yè)務(wù)場景,來說說,我們在使用過程中需要注意的點。我將分幾塊進行說明:
一、任務(wù)和場景
二、任務(wù)和鎖
以上,結(jié)合自己的一些經(jīng)驗和總結(jié),希望對大家有幫助。