quartz詳解4:quartz執行緒管理
http://blog.itpub.net/11627468/viewspace-1766967/
quartz啟動後有多個執行緒同時在跑。啟動時會啟動主執行緒、叢集執行緒、檢漏執行緒、工作執行緒。主執行緒負責查詢到需要觸發的執行緒,並放入到執行緒佇列。
叢集執行緒負責叢集、檢漏執行緒負責對未成功執行的任務進行檢漏。工作執行緒預設是20,一般PC伺服器可以調到200。
一、主執行緒QuartzScheduleThread
關於QuartzScheduleThread是quartz啟動時開始啟動,用於trigger的獲取、觸發,並放入到執行緒池中執行。
詳細可以看第4章
詳細流程如下:
關於quartz2.0版本之後可以批量執行trigger的功能。要測試是否影響定時的準確度。
二、執行緒池SimpleThreadPool
執行緒池的初使化:new 執行緒並放入執行緒池的連結串列中。
執行執行緒:把任務放到一個執行緒中執行。
執行緒結束:修改執行緒狀態。
執行緒池關閉:每個執行緒關閉,正在執行的執行緒等執行緒執行完了再關閉。
三、工作執行緒WorkThread
執行緒池中執行的工作執行緒,可以通過配置檔案quartz.properties來配置大小:
JobRunShell實現runnable介面,放入到workThread下執行:
四、叢集執行緒ClusterManager和檢漏執行緒MisfireHandler
可以看到quartz啟動時,這兩個執行緒也啟動了。
那麼,它是在什麼時候啟動的呢?是在執行QuartzScheduler的start()方法時在JobStore類上載入的。
再來分析下,這兩個執行緒的作用。
這兩個執行緒都有initialize,manage,run,shutdown方法。
--1、先看ClusterManager,在new ClusterManger後,就會觸發initialize方法,initialize方法再呼叫manager方法。
run方法的程式碼:
點選(此處)摺疊或開啟
-
public void run() {
-
while (!shutdown) {
-
if (!shutdown) {
-
long timeToSleep = getClusterCheckinInterval(
-
long transpiredTime = (System.currentTimeMillis() - lastCheckin);
-
timeToSleep = timeToSleep - transpiredTime;
-
if (timeToSleep <= 0) {
-
timeToSleep = 100L;
-
}
-
if(numFails > 0) {
-
timeToSleep = Math.max(getDbRetryInterval(), timeToSleep);
-
}
-
try {
-
Thread.sleep(timeToSleep);
-
} catch (Exception ignore) {
-
}
-
}
-
if (!shutdown && this.manage()) {
-
signalSchedulingChangeImmediately(0L);
-
}
-
}//while !shutdown
- }
而叢集的manager做的事情是判斷是否有節點down掉,同時每7.5秒傳送同步心跳修改資料庫資訊。
LAST_CHECKIN_TIME每7.5秒會更新一次。
--2、再看MisfireHandler的作用:
run方法預設每15秒執行一次。如果沒有misfire的話,則每60秒執行一次。
manager邏輯如下:
private long misfireThreshold = 60000L; //預設時間超過了1分鐘。
就是把超過1分鐘還沒執行的任務,認為是misfire,然後儲存到trigger表,等待重新再執行。
有狀態的job如果第一次沒有執行完,第二次執行的時間錯過了,就會被認為是misfire
doUpdateOfMisfiredTrigger呼叫CronTriggerImpl的updateAfterMisfire方法。
如果misfire設定為MISFIRE_INSTRUCTION_SMART_POLICY或MISFIRE_INSTRUCTION_FIRE_ONCE_NOW就是馬上執行。
如果misfire設定為MISFIRE_INSTRUCTION_DO_NOTHING則在下一個週期再執行。
預設是:MISFIRE_INSTRUCTION_SMART_POLICY
可以通過trigger的build方法增加引數實現:
withMisfireHandlingInstructionFireAndProceed().build()
withMisfireHandlingInstructionDoNothing().build()
五、總結:
從執行緒角度來分析叢集的效能的話,主要是:
1、主執行緒QuartzScheduleThread的瓶頸很可能出現在資料庫行鎖。
--可以考慮定時任務資料儲存在分散式快取。減少對資料庫的過分依賴。
2、工作執行緒WorkThread的瓶頸很可能出現在任務的阻塞。
--可以通過用非同步任務來解析,非同步任務如何放在資料庫有效能問題可以再考慮分散式快取。