1. 程式人生 > >分散式排程服務淺談

分散式排程服務淺談

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獲取到實現此介面的所有實現類,上送到排程中心。

而且,統一排程中心,還可以對所有的定時任務的排程情況進行有效監控,日誌記錄等,也可以約定介面,讓定時任務回傳定時結果,做到全域性把控的目的。

以上就是對分散式排程的一點理解,有錯誤的地方還望指正,有更好的方案也希望能分享下。

參考資料

  1. https://www.cnblogs.com/yank/p/3955322.html
  2. https://blog.csdn.net/tsyj810883979/article/details/8481621
  3. https://www.cnblogs.com/javahr/p/8318728.html
  4. http://www.quartz-scheduler.org/documentation/quartz-2.1.x/quick-start.html
  5. https://spring.io/guides/gs/scheduling-tasks/

總結

本章節主要是講解了通過不同的方式實現定時任務。對於定時任務而言,本身是門大學問,一倆篇文章是講不完的。像SpringTaskQuartz都是很強大的排程器,兩者很相似,像如何實現任務的動態修改排程週期,動態停止相關任務,排程任務的監控,這些本文章都沒有涉及。還希望有相關需求的同學自行搜尋相關資料了。