關于任務定時調度


Tags:定時作業調度 分布式定時任務調度 Quartz TBSchedule Elastic-job


基于給定時間點,給定時間間隔或者給定執行次數自動執行任務。

Timer

對于簡單的有固定間隔(period)的任務,使用JAVA內置的Timer即可解決問題。

public static void main(String[] args){  
   Timer timer = new Timer();  
   timer.schedule(new TimerTask(){
        @Override  
        public void run() {  
           System.out.println("do sth...");  
        }  
    }, 1000, 2000);  
} 

特點:in JDK簡潔,單線程

對于簡單的定時任務,Timer是非常實用的類,做一些常規的簡單任務,如在線程池中用Timer掃描出空閑線程。

ScheduledExecutor

多線程的固定間隔簡單調度,JDK也提供了工具類

public static class ScheduledExecutorTest implements Runnable {
        private String jobName = "";

        public ScheduledExecutorTest(String jobName) {
            super();
            this.jobName = jobName;
        }

        @Override
        public void run() {
            System.out.println("execute " + jobName);
        }

        public static void main(String[] args) {
            //執行線程池大小
            ScheduledExecutorService service = Executors.newScheduledThreadPool(10);

            long initialDelay1 = 1;
            long period1 = 1;
            // 從現在開始1秒鐘之后,每隔1秒鐘執行一次job1
            service.scheduleAtFixedRate(
                    new ScheduledExecutorTest("job1"), initialDelay1,
                    period1, TimeUnit.SECONDS);

            long initialDelay2 = 1;
            long delay2 = 1;
            // 從現在開始2秒鐘之后,每隔2秒鐘執行一次job2
            service.scheduleWithFixedDelay(
                    new ScheduledExecutorTest("job2"), initialDelay2,
                    delay2, TimeUnit.SECONDS);
        }
    }

特點:in JDK 多線程 線程池

Unix Crontab

相比較Timer這種固定間隔調度,crontab的可以使用cron表達式表達更復雜調度策略:

每1分鐘執行一次myCommand
* * * * * myCommand

實例2:每小時的第3和第15分鐘執行
3,15 * * * * myCommand

實例3:在上午8點到11點的第3和第15分鐘執行
3,15 8-11 * * * myCommand

實例4:每隔兩天的上午8點到11點的第3和第15分鐘執行
3,15 8-11 */2  *  * myCommand

crontab往往和腳本搭配完成更復雜的任務,意味著當需要和主系統進行復雜交互時多有不便。

特點: Linux內置 Crontab表達式

Quartz##

Quartz是個開源JAVA庫,可以簡單看做以上三種的結合的擴展。

Quartz組件圖

Scheduler:調度容器
Job:Job接口類
JobDetail :Job的描述類,job執行時的依據此對象的信息反射實例化出Job的具體執行對象。
Trigger:存放Job執行的時間策略
JobStore: 存儲作業和調度期間的狀態
Calendar:指定排除的時間點(如排除法定節假日)

Quartz線程圖

Quartz的主要線程有兩類,負責調度的線程和負責Misfire(指錯過了執行時間的作業)的線程,其中負責調度的線程RegularSchedulerThread是基于線程池的,而Misfire只有一個線程。 兩類線程都會訪問抽象為JobStore的層來獲取作業策略或寫入調度狀態。
JobStore也分持久化(JobStoreSupport)和非持久化(RAMJobStore)兩種,使用場景大大不同,后面有敘述。

Quartz的觸發

注意上圖左邊部分是調度器的守護線程QuartzScheduleThread的主要流程,也就是:QuartzScheduleThread會在RegularThread池有空閑時(否則block),從JobStore中取出N個(將來30秒內要觸發的)Trigger,并交給RegularThread線程池來運行job。

Quartz的功能非常豐富,結構也比上述的復雜的多,本文只是簡要介紹抽象層的概念,詳解請參考更多資料。
對于單機調度Quartz基本能完全滿足我們的需求,但多個機器怎么辦呢?

Quartz集群##

為了分擔單點壓力,往往需要多個節點運行定時任務,他們之間有協作又不能沖突。

Quartz集群部署圖

Quartz用了一個比較取巧的方式支持集群定時調度。

Quartz使用持久化的線程模型

首先,JobStore要選用數據庫持久化存儲:JDBCJobStore,且自己管理事務:JobStoreTX。

依附于本身的trigger存取策略,Quartz利用數據庫行級鎖來實現多節點的通訊(間接通訊)。

0.調度器線程run()

1.獲取待觸發trigger

    1.1數據庫LOCKS表TRIGGER_ACCESS行加鎖

    1.2讀取JobDetail信息

    1.3讀取trigger表中觸發器信息并標記為"已獲取"

    1.4commit事務,釋放鎖

2.觸發trigger

    2.1數據庫LOCKS表STATE_ACCESS行加鎖

    2.2確認trigger的狀態

    2.3讀取trigger的JobDetail信息

    2.4讀取trigger的Calendar信息

    2.3更新trigger信息

    2.3commit事務,釋放鎖

3實例化并執行Job

    3.1從線程池獲取線程執行JobRunShell的run方法
    

讀取之前獲取鎖,寫入之后釋放鎖,這是Quartz集群解決集群同步的核心思想。

Quartz集群是用工具拼湊起來的一個方案,巧妙的運用了數據庫鎖解決同步問題,這在一些場景中是非常work的,但問題也依舊明顯:
解決了節點同步問題,但沒有解決分布式問題。

官方也做出說明,集群特性對于高cpu使用率的任務效果很好,但是對于大量的短任務,各個節點都會搶占數據庫鎖,這樣就出現大量的線程等待資源.這種情況隨著節點的增加會越來越嚴重.

有沒有解決分布式問題的方案?

TBSchedule##

TBSchedule部署圖

類比Quartz集群用數據庫做存儲,TBSchedule則使用更符合分布式場景的zookeeper來做任務狀態。

zookeeper有永久節點存儲作業的配置信息,使用臨時節點存儲調度時的狀態,當其中一個調度端和zookeeper斷開鏈接時,回話消失臨時節點數據被抹除,所有在線調度端會感知到改變化并做出相應的動作。

來看幾個重要概念:

  • 任務項
    即分片。分布式機制是通過分片實現:
    如:TaskItem: 0,1,2,3
    可以用數據的ID取模對應TaskItem,一個TaskItem就代表了一部分 數據。
    如上線了機器[A,B,C], TBScher會做如下分配:
    [A=1,0,B=2,C=3]

如上線了機器[A,B,C,D,E], TBScher會做如下分配:
[A=0,B=1,C=2,D=3],E空閑。

分片操作由是leader節點執行,leader是最早上線的節點(編號最?。?。

  • 節點感知
    調度端會啟動一個刷新zookeeper的timer,如果有變動則回觸發leader的重新分配資源,
    如:
    新上線或下線了機器,會給各個調度端重新分配TaskItem。
    暫停或重新啟動某個策略,調度端會停止之前的負責這個策略的線程組。

  • 觸發

TBScheduler依舊支持Crontab表達式,并進一步支持執行的時間段(超過時間段則暫停),

但其內里實現有異于Quartz:

對于一個策略,在首次啟動時會計算出該策略的下次執行開始時間和執行結束,然后分別啟動一個負責啟動和暫停的Timer,Timer內的操作就是對調度器的暫停和恢復,以及下一批Timer的創建。

TBScher的流式Job###

相對于Quartz的job只有execute,Tbscher的Job主要多了selectTasks()方法。

/**
 * 單個任務處理實現
 * 
 * @author xuannan
 * 
 */
public class DemoTaskBean implements IScheduleTaskDealSingle<Long> {

    public List<Long> selectTasks(String taskParameter,String ownSign, int taskItemNum,
            List<TaskItemDefine> queryCondition, int fetchNum) throws Exception {
        List<Long> result = new ArrayList<Long>();
        String message = "獲取數據...[ownSign=" + ownSign + ",taskParameter=\"" + taskParameter +"\"]:";
        return result;
    }

    public boolean execute(Long task, String ownSign) throws Exception {
        Thread.sleep(50);
        log.info("處理任務["+ownSign+"]:" + task);
        return true;
    }
}

selectTasks返回的結果會被帶入execute中執行,當execute時task為空時會再次selectTasks,
一次調度中,selectTasks可能會被調用多次,直到返回空,結束本次調度。

TBSchedule的出現最大的進步之處在于從關注作業到關注數據。在此概念上造就了高性能,也真正解決了集群分布式問題。

缺點:

  1. 對zookeeper的操作都是原生客戶端的直接操作,維護起來易出錯外,zookeeper的高可用也沒有良好支持。zookeeper掛掉要重啟所有調度端。
  2. 文檔缺失,四年內沒有任何更新(2016),缺少開源社區的維護。

Elastic-job##

Elatic-job部署圖

原理基本和TBSchedule一致。

一些重要概念:

  • leader選舉
    調度端機器上線后會檢查有沒有leader,如果沒有則提議自己做leader,兩個同時上線引發沖突是由zookeeper的內部解決的,總之它可以保證只有一個主。
    leader如果下線會觸發重新選舉,在選出下個leader前所有任務會被阻塞。

  • 分片
    leader選舉后,leader以『協調者』角色負責分片,同時依賴zookeeper的臨時節點和監聽器的主動檢查和通知功能,對機器上、下線、任務配置更改、分片修改等事件做出響應。

任務的設計###

因為借助Quartz做實際調度工作,所以Elatic-job的任務都是Quartz的Job的實現,但做了更多的細分擴展:

  1. 簡單任務:
    AbstractSimpleElasticJob
    類似Quartz的Job,在Elastic-job的意義則多了高可用。

  2. 流式任務:
    AbstractDataFlowElasticJob
    類似TBSchedule的任務,又再次基礎細分重視順序的AbstractSequenceDataFlowElasticJob和重視性能的AbstractThroughputDataFlowElasticJob。

  3. 用戶擴展任務
    elatic-job是向著插件化看齊的,希望用戶以插件形式貢獻代碼,編寫更多有用的任務。

一些亮點###

  1. Sharding Offset
    框架提供了記錄當前處理位移的方式,這往往用于大批量的任務處理中機器掛掉,這時候別的機器接手了掛掉的機器的任務時,需要知道哪些任務處理過了哪些還沒處理。在TBSchedule中需要自己在自己的系統中做持久化標記,而在Elatic-job中則可以使用Sharding Offset,這為failover提供了便利。

  2. Misfire開關
    本次作業開啟后上次作業因為某種原因還沒有結束,框架把這次作業標記為Misfire,上次作業執行完后會彌補標記了Misfire的作業。
    Quartz中原本也有Misfire,但在分布式環境中使用Misfire需要另外的支持,Elatic-job引入了它。

Elastic-job是2015年當當網發布的開源項目,它出現的意義是對TBSchedule在各方面的優化,這體現在它借鑒了TBSchedule的流式任務概念,但基本的調度功能還是交給這方面的資深專家:Quartz,而對zookeeper的操作使用crutor封裝,以及文檔比較全面,這一點對于維護者來說是心頭好。

唯一的缺點是太新,缺少線上環境的考驗。但當當的開發者在推廣方面很給力,贊一個。

總結##

本文從淺至深的介紹了任務調度技術,但沒有使用說明和結構詳解,因為本文旨在對比的基礎上做原理介紹,可以在技術選型上給出參考。

參考資料##

https://www.ibm.com/developerworks/cn/java/j-lo-taskschedule/
http://tech.meituan.com/mt-crm-quartz.html
http://www.cnblogs.com/davidwang456/p/4205237.html
http://code.taobao.org/p/tbschedule/wiki/index/
https://github.com/dangdangdotcom/elastic-job

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,362評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,013評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,346評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,421評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,146評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,534評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,585評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,767評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,318評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,074評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,258評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,828評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,486評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,916評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,156評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,993評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,234評論 2 375

推薦閱讀更多精彩內容