升級@Scheduled-分佈式定時任務

最近我在對項目的定時任務服務升級,但願改形成分佈式,本來是利用@Scheduled註解實現,然而它並不支持分佈式,若是改爲quartz或者Spring Cloud Task,感受對於本身這個簡單的項目也沒有必要。所以,我準備手寫一個簡單的支持分佈式定時調度任務的框架。 git

項目地址是github.com/death00/dis…,歡迎你們star、提意見。github

分析

先分析了一下本身的項目,全都是用的cron表達式,所以執行時間點都是固定的,若是升級爲分佈式的話,確定是但願在同一個時間點只有一個應用去執行定時調度。數據庫

場景就變成了:框架

多個應用在同一個時間都嘗試去執行任務,但最終只有一個應用真正執行。分佈式

這樣的話,立馬就會讓人聯想到使用去解決,由於是多個應用,因此就是分佈式鎖。那麼,場景又變了:spa

多個應用在同一個時間都嘗試去獲取分佈式鎖,只有一個應用能搶到這把鎖,搶到鎖的應用能夠執行定時任務,其餘應用則直接放棄,等待下一次執行時間。3d

搶鎖的時機是每次定時任務執行以前,這又讓我聯想到了AOP,那麼利用註解也就瓜熟蒂落了。code

分佈式鎖

既然談到了分佈式鎖,那麼就想一下,這把鎖的名稱構成是什麼。由於定時任務都有本身專門的時間,若是僅僅採用時間的話,那麼當有兩個任務同時執行時,則就是在搶一把鎖,這一樣是不合理的。cdn

因此,鎖的名稱由兩部分組成:任務執行時間、任務名稱。索引

實現

實現方案其實已經很成熟了,能夠利用Redis數據庫Zookeeper等,Redis用的命令是setNx數據庫通常都是利用的惟一索引Zookeeper這點我也不是很瞭解(若是有感興趣的同窗,歡迎在個人項目中添加)。

個人項目中實現了Redis數據庫兩種方式,能夠看類DisScheduleRedisServiceImplDisScheduleMongodbServiceImpl

註解

其次,我自定義了一個註解DisSchedule

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DisSchedule {

    /**
     * 定時調度任務的名稱(默認是方法名)
     */
    String name() default "";

    /**
     * 任務的間隔時間
     */
    int duration();

    /**
     * duration的時間單位(默認:分鐘)
     */
    DisScheduleUnit unit() default DisScheduleUnit.MINUTES;
}複製代碼

  1. name表明這次定時調度任務的名稱。
  2. duration表明任務的間隔時間,配合unit
  3. unit是自定義的時間單位,有秒、分鐘。

該註解須要配合@Scheduled共同使用,例如:

@DisSchedule(name = "testSchedule", duration = 1, unit = DisScheduleUnit.MINUTES)
    @Scheduled(cron = "0 0/1 * * * ?")複製代碼

cron表達式表明1分鐘執行一次,且是在整數分鐘開始的時候執行,所以@DisSchedule也須要設置爲1分鐘的時間。

切面

接下來,咱們只須要在Aspect中定義好切入點(有註解@DisSchedule的方法上),針對這些方法,須要使用Around(環繞加強)進行攔截,由於當搶不到鎖的時候,就不容許執行。

具體能夠參考類DisScheduleAspect

總結

以上就是我實現的簡單的分佈式定時任務,雖然簡單,但應該能夠知足你的基礎需求,接下來,我會在這個之上,逐步增長功能(好比監測、失敗後預警等)。若是你有什麼想法,歡迎在下方留言。

有興趣的話能夠訪問個人博客或者關注個人公衆號、頭條號,說不定會有意外的驚喜。

death00.github.io/

相關文章
相關標籤/搜索