分散式排程服務淺談
在
JAVA
開發領域,目前可以通過以下幾種方式進行定時任務:
- Timer:jdk中自帶的一個定時排程類,可以簡單的實現按某一頻度進行任務執行。提供的功能比較單一,無法實現複雜的排程任務。
- ScheduledExecutorService:也是jdk自帶的一個基於執行緒池設計的定時任務類。其每個排程任務都會分配到執行緒池中的一個執行緒執行,所以其任務是併發執行的,互不影響。
- Spring Task:
Spring
提供的一個任務排程工具,支援註解和配置檔案形式,支援Cron
表示式,使用簡單但功能強大。ThreadPoolTaskScheduler
- Quartz:一款功能強大的任務排程器,可以實現較為複雜的排程功能,如每月一號執行、每天凌晨執行、每週五執行等等,還支援分散式排程,就是配置稍顯複雜。
在單機模式下,定時任務是沒什麼問題的。但當我們部署了多臺服務,同時又每臺服務又有定時任務時,若不進行合理的控制在同一時間,只有一個定時任務啟動執行,這時,定時執行的結果就可能存在混亂和錯誤了。
這裡簡單的說說相關的解決方案吧,一家之言,希望大家能提出自己的見解,共同進步!
- 剝離所有定時任務到一個工程:此方案是最簡單的,在定時任務相對較小,併發任務不多時,可以使用此方案。簡單也容易維護。當定時任務牽扯的業務越來越多,越來越雜時,維護量就成本增加了,工程會越來越臃腫,此方案就不實用了。
- 利用
Quartz
叢集方案:本身Quartz
是支援通過資料庫實現叢集的,以下是其叢集架構圖:
其實現原理也相對簡單:通過資料庫實現任務的持久化,儲存定時任務的相關配置資訊,以保證下次系統啟動時,定時任務能自動啟動。同時,通過資料庫行鎖(for update)
機制,控制一個任務只能被一個例項執行,只有獲取鎖的例項才能執行任務,其他的只能等待,直到鎖被釋放。這種方式有些弊端,就是依賴了資料庫,同時也需要保證各伺服器之間的時間需要同步,不然也是會混亂的。
現在Quartz
也有基於Redis
的叢集方案,有興趣的可以搜尋下。
- 分散式鎖:可通過使用
Redis
或者ZooKeeper
實現一個分散式鎖的機制,使得只有獲取到鎖的例項方能執行定時任務,避免任務重複執行。可檢視下開源的基於Redis
redisson
。github地址:https://github.com/redisson/redisson有興趣的同學可以瞭解下。 - 統一排程中心:
可構建一個純粹
的定時服務,只有定時器
相關配置,比如定時時間
,定時排程的api介面
或者http
服務,甚至是統一註冊中心下的服務類,如dubbo服務等。而具體的任務執行操作都在各自業務方系統中,排程中心只負責介面的呼叫
,具體實現還是在業務方。這種方案相對來說比較通用,實現起來也簡單。就是需要業務方進行約定程式設計,或者對外提供一個api介面。
當然,為了實現定時任務的自動發現和註冊功能,還是需要規範一套規則來實現自動註冊功能。簡單來說,以Dubbo
服務為例,可以定義一個定時任務介面類
,排程中心只需要獲取所有實現此介面的服務,同時通過服務的相關配置(排程時間、失敗策略等)進行相關定時操作。或者編寫一個服務註冊與發現的客戶端,通過Spring
獲取到實現此介面的所有實現類,上送到排程中心。
而且,統一排程中心,還可以對所有的定時任務的排程情況進行有效監控,日誌記錄等,也可以約定介面,讓定時任務回傳定時結果,做到全域性把控的目的。
以上就是對分散式排程的一點理解,有錯誤的地方還望指正,有更好的方案也希望能分享下。
參考資料
- https://www.cnblogs.com/yank/p/3955322.html
- https://blog.csdn.net/tsyj810883979/article/details/8481621
- https://www.cnblogs.com/javahr/p/8318728.html
- http://www.quartz-scheduler.org/documentation/quartz-2.1.x/quick-start.html
- https://spring.io/guides/gs/scheduling-tasks/
總結
本章節主要是講解了通過不同的方式實現定時任務。對於定時任務而言,本身是門大學問,一倆篇文章是講不完的。像
SpringTask
和Quartz
都是很強大的排程器,兩者很相似,像如何實現任務的動態修改排程週期,動態停止相關任務,排程任務的監控,這些本文章都沒有涉及。還希望有相關需求的同學自行搜尋相關資料了。