Quartz:任務排程實現原理
什麼是Quartz
Quartz是一個完全由Java編寫的開源作業排程框架,為在Java應用程式中進行作業排程提供了簡單卻強大的機制。
Quartz允許開發人員根據時間間隔來排程作業。
它實現了作業和觸發器的多對多的關係,還能把多個作業與不同的觸發器關聯。簡單地建立一個org.quarz.Job介面的Java類。
Quartz的特點
作為一個優秀的開源排程框架,Quartz 具有以下特點:
① 強大的排程功能,例如支援豐富多樣的排程方法,可以滿足各種常規及特殊需求;
② 靈活的應用方式,例如支援任務和排程的多種組合方式
,支援排程資料的多種儲存方式;③ 分散式和叢集能力,Terracotta 收購後在原來功能基礎上作了進一步提升。
④ Quartz 很容易與 Spring 整合實現靈活可配置的排程功能。
Quartz專用詞彙說明
下面是本文中用到的一些專用詞彙,在此宣告:
scheduler:任務排程器
trigger:觸發器,用於定義任務排程時間規則
job:任務,即被排程的任務
misfire:錯過的,指本來應該被執行但實際沒有被執行的任務排程
Quartz任務排程基本實現原理:
Quartz任務排程的核心元素為:Scheduler——任務排程器、Trigger——觸發器、Job——任務。
其中trigger和job是任務排程的元資料,scheduler是實際執行排程的控制器。
Trigger
- 是用於定義排程時間的元素,即按照什麼時間規則去執行任務。
- Quartz中主要提供了四種類型的trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和NthIncludedDayTrigger。
- 這四種trigger可以滿足企業應用中的絕大部分需求。
Job
- 用於表示被排程的任務。
- 主要有兩種型別的job:無狀態的(stateless)和有狀態的(stateful)。
- 對於同一個trigger來說,有狀態的job不能被並行執行,只有上一次觸發的任務被執行完之後,才能觸發下一次執行。
- Job主要有兩種屬性:volatility和durability,其中volatility表示任務是否被持久化到資料庫儲存,而durability表示在沒有trigger關聯的時候任務是否被保留。兩者都是在值為true的時候任務被持久化或保留。
- 一個job可以被多個trigger關聯,但是一個trigger只能關聯一個job。
Scheduler
- scheduler由scheduler工廠建立:DirectSchedulerFactory或者StdSchedulerFactory。
- StdSchedulerFactory使用較多,因為DirectSchedulerFactory使用起來不夠方便,需要作許多詳細的手工編碼設定。
- Scheduler主要有三種:RemoteMBeanScheduler,RemoteScheduler和StdScheduler。StdScheduler 為最常用。
Quartz 執行緒檢視
在Quartz中,有兩類執行緒,Scheduler排程執行緒和任務執行執行緒,其中任務執行執行緒通常使用一個執行緒池維護一組執行緒。
Scheduler排程執行緒主要有兩個:執行常規排程的執行緒,和執行misfiredtrigger的執行緒。
常規排程執行緒:輪詢儲存的所有trigger,如果有需要觸發的trigger,即到達了下一次觸發的時間,則從任務執行執行緒池獲取一個空閒執行緒,執行與該trigger關聯的任務。
Misfire執行緒:是掃描所有的trigger,檢視是否有misfiredtrigger,如果有的話根據misfire的策略分別處理(fire now OR wait for the next fire)。
Quartz Job資料儲存
Quartz中的trigger和job需要儲存下來才能被使用。
Quartz中有兩種儲存方式:RAMJobStore,JobStoreSupport。
RAMJobStore是將trigger和job儲存在記憶體中,而JobStoreSupport是基於jdbc將trigger和job儲存到資料庫中。
RAMJobStore的存取速度非常快,但是由於其在系統被停止後所有的資料都會丟失,所以在叢集應用中,必須使用JobStoreSupport。
任務排程器排程的時序:
在這裡將幾個重要的類呼叫的過程以序列圖的形式展現出來,上半部分展現的是啟動過程,下半部分展現的是任務排程的過程。
步驟1.使用者首先需要生成一個排程器工廠SchedulerFactory,可以用下面的方式實現自己的定製化:
Properties properties=new Properties();
properties.put("org.quartz.threadPool.class","org.quartz.simpl.SimpleThreadPool");
properties.put("org.quartz.threadPool.threadCount","10");
SchedulerFactory sf=new StdSchedulerFactory(properties);
步驟2.然後通過getScheduler()方法從排程器工廠裡得到排程器例項,首先查詢有沒有這樣的排程器,沒有的話,就生成一個,有的話直接返回。所以得到的一般是單例,即預設的排程器。
步驟3.Scheduler有一個QuartzSchedulerThread(Thread的子類)屬性,在scheduler例項化的時候,例項化了一個物件,並用ThreadExecutor啟動該執行緒物件。該執行緒就是排程執行緒,主要任務就是不停的從JobStore中獲取即將被觸發的觸發器(預設30s排程一次)。在這個時候排程執行緒雖然啟動,但是處於pause狀態。
步驟4.接下來是任務排程的部分:
Scheduler scheduler=sf.getScheduler();
scheduler.addJobListener(new TaskListener());
scheduler.scheduleJob(jobDetail, simpleTrigger);
scheduler.start();
client通過scheduleJob()方法將任務和觸發器儲存在JobStore中,通過start()方法將QuartzSchedulerThread的pause狀態設為false,通知排程執行緒執行任務,此後排程執行緒不停的從JobStore中去取即將觸發的任務。
任務執行的時序:
上半部分展現的是任務執行之前準備工作的時序,下半部分展現的是任務執行的時序。
步驟1.排程執行緒首先去執行緒池中獲取可用的執行緒,如果沒有的話,就阻塞。
步驟2.從JobStore(從儲存介質中獲取觸發器,儲存介質可以是記憶體也可以是資料庫)獲取(接下來30s內的)觸發器,然後等待該觸發器觸發。
步驟3.排程執行緒建立一個JobRunShell(就是一個Runnable),然後從執行緒池中呼叫執行緒執行該任務。
接下來就是任務執行的時序:
步驟4.獲取trigger、JobDetail以及生成Job例項,然後執行job的execute介面函式。
持久化的任務的執行時序:
以上就是Quartz的基本工作流程。
參考來源:https://www.cnblogs.com/zhangchengzhangtuo/p/5705672.html