摘要spring
問題:從單機擴展到集羣數據庫
方案一:不作改造,直接擴展緩存
方案二:多處調度、一處執行服務器
方案三:一處調度、一處執行網絡
方案四:一處調度、多處執行負載均衡
方案五:多處調度、多處執行分佈式
摘要ide
從改造工做量、可用性、負載均衡、資源利用等方面,簡單介紹了幾種集羣環境下定時任務調度的方案。性能
問題:從單機擴展到集羣spa
單機環境的定時任務很簡單。不管是用比較原始的Timer,仍是用自成體系的quartz、spring-scheduler,均可以輕鬆寫意的實現功能。
可是,當應用水平擴展到集羣環境下時, 定時任務會出現重複調度、重複執行,可能帶來資源浪費、數據錯誤等問題。
方案一:不作改造,直接擴展
對有些定時任務來講,重複調度、重複執行並不構成大問題。例如刪除過時數據任務、數據監控任務等,除了會形成必定的資源浪費以外,其實無傷大雅。
從功能上來講,只有嚴格冪等的任務才能夠直接擴展。性能方面來講,這個方案適合對網絡、內存等資源壓力小的定時任務。另外,若是定時任務須要處理的資源自己不在集羣服務之間共享,那麼必然要使用這個方案:例如檢查服務本地緩存數據過時狀況的定時任務就屬於這種狀況。
工做量
因爲不須要對單機環境的定時任務進行改造,所以這個方案的改造工做量是很是小的。
只不過,在開發單機環境的定時任務時就要重點考慮如何保證任務的冪等性,這多是一項額外的工做。
可用性
高可用性是沒的說的。集羣中的每臺服務都有一套獨立、完整的任務調度、執行功能,只要有一臺服務存活,就能夠保證任務完整執行。
負載均衡
在負載均衡方面,這個方案是最差的:它其實是「負載翻倍」。每臺服務器須要都承擔一個任務的所有壓力;對網絡、數據庫等共享資源來講,壓力更是N倍增加。因此前面纔會提到:這個方案適合對網絡、內存等資源性能壓力小的任務。
資源利用
若是說這個方案在負載均衡方面得分爲0的話,資源利用上就是負分。
方案二:多處調度、一處執行
方案一能夠說是「多處調度、多處執行」,方案二則是「多處調度、一處執行」。這種方案的基本思路是:雖然多臺服務同時運行調度機制,但經過某種機制來保證只有一臺服務能最終執行任務。這種機制能夠是quartz的集羣調度功能,也能夠是zookeeper的多活選主機制,還能夠是分佈式鎖機制。
因爲這個方案保證「一處執行」,所以它並不要求定時任務具備冪等性,適用性更廣。
工做量
總體而言,這個方案只改造定時任務的調度機制,不涉及執行機制。而對於定時任務來講,調度機制比較統一,執行功能則變化更多。所以,儘管工做量與實際使用的機制有關,但這個方案並不算太麻煩。
以我此前參與過的一個的定時任務多活改造爲例,從使用spring-scheduler的單機調度功能改造爲quartz的集羣調度功能,工做量、工期、風險等都在可控範圍以內。
可用性
只要實現了集羣部署,可用性必定是上了一個臺階的。
不過,通常來講,這個方案總會引入一個第三方機制來決定由哪臺服務來執行任務(quartz使用數據庫、zookeeper使用ZK服務等)。這個第三方機制,不管多麼可靠、可用,多多少少仍是會引入一些不可用風險。例如使用zookeeper選主機制,若是由於網絡、機房等緣故致使選主失敗,進而使得「多處調度」以後「零處執行」,也會使得定時任務引起問題。
另外,「一處執行」也會增長可用性風險。若是某個定時任務因爲某臺服務自身的問題執行失敗,咱們須要額外的機制(如spring-batch的restart機制)來處理。
只不過這種風險機率很是低,大部分時候咱們都直接忽略掉了。
負載均衡
負載均衡是這個方案的一個「黑點」。因爲只能保證「一處執行」,同一個定時任務的全部壓力都在這一臺服務上;其它服務即便空閒、可用,也只能袖手旁觀。
可是,這個方案能夠把不一樣的定時任務「負載均衡」到不一樣的服務上執行,從而避免全部任務都在同一臺服務上執行的極端狀況。
資源利用
簡單分析一下,咱們能夠知道,當不一樣任務的資源佔用(內存/cpu/網絡等資源的使用量,以及資源佔用時間)比較平均時,這個方案對資源的利用率比較高。可是若是各任務間相差較大(如任務A執行時間1小時,任務B執行時間2分鐘),就會形成必定的資源浪費。
方案三:一處調度、一處執行
方案二的調度機制會在多臺服務上同時運行,所以也能夠稱之爲「分佈調度,一處運行」。方案三則將調度機制集中到一套調度服務上,由調服服務進行「集中調度」,而後再由應用服務「一處執行」。
關於這種方案已有很多實現,如ELASTIC-JOB等等。不過其中有一些方案,其實是「多處調度、一處執行」。按下不表。
工做量
這種方案的改造量會比較大。
雖然定時任務能夠分爲調度和執行兩部分,但大部分狀況下,這兩部分代碼結合得都比較緊密。方案三其實是將「調度」功能與「執行」工做剝離開(有時甚至會把兩者部署爲不一樣的服務)。拆分原有代碼的工做量可見一斑。
可用性
參見方案二。
負載均衡
參見方案二。
資源利用
參見方案二。
方案四:一處調度、多處執行
方案四是方案三的一個擴展。在方案三把「調度」與「執行」拆開之後, 這個方案開始把觸角伸向了「執行」功能,並將「執行」功能拆分紅「讀取-處理」兩部分(是的,參考了spring-batch的reader-processer-writer模式)。
在方案四中,「調度」功能只是調度「讀取」功能,即在指定的時間點讀取出全部須要處理的數據。讀出數據以後,利用消息隊列等機制,將數據分給多個「處理」服務。
在咱們系統中就有相似機制。某個任務使用的是quartz的集羣部署方案來保證「一處調度」;任務運行時,會先讀取出當天須要處理的數據,並將其數據發送到ActiveMQ的隊列中,交由相關功能來處理。
工做量
這個方案不只要把「調度」功能單獨拆出來,還要把「執行」功能再次拆分爲「讀取」和「處理」,並分別進行部署。其中的難度可想而知。
可用性
相比方案二和方案三,這個方案引入了更多的外部依賴。因此至少理論上,它的可用性(可靠性?)是最低的。
負載均衡
這個方案的負載均衡能力來自於將「讀取」到的數據分發給「處理」服務時所使用的機制。若是使用消息隊列(如ActiveMQ),則它也能將任務負載均衡地分發給集羣中的多臺服務。
資源利用
對定時任務來講,這個方案能夠至關充分地利用系統資源。
可是,對消息隊列、網絡等「額外」資源來講,這個方案在很大程度上會加劇它們的負擔。
方案五:多處調度、多處執行
雖然方案一也是「多處調度、多處執行」,但方案五跟它是有差異的。
方案五將「執行」機制進一步拆分爲「讀取」、「判斷」、「執行」三部分。首先讀取定時任務所需處理的數據全集;而後利用分佈式鎖等機制,逐個判斷讀取到的某條數據是否應當由當前服務執行;只有經過了判斷的數據纔會進入到執行階段。
相比「執行」,方案五對「調度」機制基本沒作處理。單機環境下怎麼調度,集羣環境下仍然怎麼調度。
工做量
方案五不變動「調度」機制,改造工做量全在「執行」機制上。而對執行機制的改造也只是「插入」一個步驟,而並不修改、拆分基本流程。這種改造工做相對來講仍是比較輕鬆的。
可用性
一樣實現了集羣部署,方案五的可用性是有保證的。因爲調度、執行都在多臺服務上同時運行,不管哪一臺服務出現問題,其它服務仍能正常的調度和執行任務,受到影響的可能只是那臺服務上正在處理的一條(一批)數據。
而且,它對外部環境的依賴也比較小(基本也就引入了一個分佈式鎖),相對其它方案來講,風險也更低。
負載均衡
懶得敲字了。
資源利用
方案五至關於把一個任務所需處理的數據平均分紅N份,均勻的交給集羣中的服務來處理。所以,它對資源的利用率能夠說是最高的。
不過,在讀取數據階段,因爲要讀取一個「全集」,可能會帶來一些資源壓力。