SpringCloud架構系統中如何保證集群環境下定時任務同時只有一個實例運行工作?



問題

首先說下情況,我們平常開發SpringCloud微服務的時候,若要確保高可用,同一服務都會部署多台實例,然后注冊到Eureka上。

一般我們會把所有定時任務寫到一個服務里,那平常單實例的時候,都可以正常執行。如果該定時任務服務部署多個實例,如何確保只在一個服務實例里執行任務呢?

個人總結了下,可以有以下解決思路。

解決

1. 如果原有的task代碼同時執行一次或多次的結果都是正確的,那么可以就不做任何處理,只不過會造成資源浪費,當然這種方式不推薦。

比如圖里的taskA是每隔五分鍾更新下已完成的工單的記錄為歸檔,那其實本質就是執行一個update的sql,即使是同時執行mysql數據庫也有鎖機制,所以數據不會出錯。那多實例下也可以不做處理,不過再說一遍,雖然結果不會出錯,但是不推薦這樣做,還是要處理一下的。

2. 使用分布式鎖

借助分布式鎖,確保多個實例里的task只有競爭到鎖的實例任務才執行。比如,redis的分不式鎖。這種方式不好的地方是需要修改邏輯代碼,增加了對redis的依賴。

3. 使用任務調度框架的集群功能

之前我使用的是Quartz,Quartz是一個完全由Java編寫的開源作業調度框架,Quartz的集群功能通過故障轉移和負載平衡功能為您的調度程序帶來高可用性和可擴展性。這種方式也有不好的地方,那就是要實現Quartz的集群功能,需要修改Quartz的配置,而且是要額外增加Quartz集群需要的數據庫表,如果一開始開發沒有考慮集群,后面再加入改動會有點大。

4. 最小ip執行

這應該算是一個思路,我也是在網上看到的,具體實現是。

①先在代碼里獲取獲取到當前實例的ip,通過一定算法規則轉成一個Long型。
②從Eureka里根據實例名,圖里的Taks-Server獲取到對應的集群ip集合,也就是192.168.2.10、192.168.2.11、192.168.2.12,也分別把它們轉成對應的Long型。
③拿第一步里的Long和第二步獲取的Long的List做對比,如果判斷到它是集合里最小的,那就在該實例里執行task,否則就retur掉。

這樣就能確保永遠只有一個實例執行定時任務了。

5. elastic-job

elastic-job 是由當當網基於quartz 二次開發之后的分布式調度解決方案,所以本質和方法3一樣。

總結

如果你的系統架構剛開始,那就可以把任務的分布式集群情況考慮進去,使用集群框架實現,如果你的系統已經完成或趨於穩定,則不建議大改,可以考慮方法2的分布式鎖或者方法3。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM