quartz2.3任務管理的原始碼分析
概述
quartz在任務管理中,先把任務存在某個地方(QuartzSchedulerResources),等到時機到了(觸發trigger),再由排程執行緒(QuartzSchedulerThread)把該任務取出來放到執行緒池中執行。
因為主執行緒的QuartzScheduler會操作QuartzSchedulerResources,把任務存入quartz;排程執行緒也會操作QuartzSchedulerResources,從中取出任務。所以QuartzSchedulerResources必須是單例模式,或者QuartzScheduler和QuartzSchedulerThread引用的是同一個QuartzSchedulerResources物件。
QuartzScheduler、QuartzSchedulerResources和QuartzSchedulerThread的關係
QuartzScheduler
private QuartzSchedulerResources resources;
private QuartzSchedulerThread schedThread;
public QuartzScheduler(QuartzSchedulerResources resources, long idleWaitTime, @Deprecated long dbRetryInterval) throws SchedulerException {
this.resources = resources;
this.schedThread = new QuartzSchedulerThread(this, resources);
.......
}
QuartzSchedulerResources
//SimpleThreadPool的介面,quartz執行緒管理可以看上一篇部落格
private ThreadPool threadPool;
//儲存job的地方
private JobStore jobStore;
QuartzSchedulerThread
private QuartzScheduler qs;
private QuartzSchedulerResources qsRsrcs;
把任務加入QuartzSchedulerResources
QuartzScheduler#scheduleJob()
把任務加入quartz,底層是存入QuartzSchedulerResources 的jobStore
/**
* <p>
* Add the <code>{@link org.quartz.Job}</code> identified by the given
* <code>{@link org.quartz.JobDetail}</code> to the Scheduler, and
* associate the given <code>{@link org.quartz.Trigger}</code> with it.
* </p>
*
* <p>
* If the given Trigger does not reference any <code>Job</code>, then it
* will be set to reference the Job passed with it into this method.
* </p>
*
* @throws SchedulerException
* if the Job or Trigger cannot be added to the Scheduler, or
* there is an internal Scheduler error.
*/
public Date scheduleJob(JobDetail jobDetail,
Trigger trigger) throws SchedulerException {
validateState();
.........
OperableTrigger trig = (OperableTrigger)trigger;
if (trigger.getJobKey() == null) {
trig.setJobKey(jobDetail.getKey());
} else if (!trigger.getJobKey().equals(jobDetail.getKey())) {
throw new SchedulerException(
"Trigger does not reference given job!");
}
trig.validate();
Calendar cal = null;
if (trigger.getCalendarName() != null) {
cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName());
}
Date ft = trig.computeFirstFireTime(cal);
if (ft == null) {
throw new SchedulerException(
"Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
}
//QuartzSchedulerResources儲存job和trigger
resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
notifySchedulerListenersJobAdded(jobDetail);
notifySchedulerThread(trigger.getNextFireTime().getTime());
notifySchedulerListenersSchduled(trigger);
return ft;
}
QuartzSchedulerThread取出任務放入執行緒池執行
1、從QuartzSchedulerResources 中取出trigger
2、根據到達指定時間的trigger從QuartzSchedulerResources中取出相應的job
3、呼叫ThreadPool#runInThread()方法把任務放入執行緒池中執行。
/**
* <p>
* The main processing loop of the <code>QuartzSchedulerThread</code>.
* </p>
*/
@Override
public void run() {
int acquiresFailed = 0;
while (!halted.get()) {
try {
// check if we're supposed to pause...
synchronized (sigLock) {
while (paused && !halted.get()) {
try {
// wait until togglePause(false) is called...
sigLock.wait(1000L);
} catch (InterruptedException ignore) {
}
// reset failure counter when paused, so that we don't
// wait again after unpausing
acquiresFailed = 0;
}
if (halted.get()) {
break;
}
}
// wait a bit, if reading from job store is consistently
// failing (e.g. DB is down or restarting)..
if (acquiresFailed > 1) {
try {
long delay = computeDelayForRepeatedErrors(qsRsrcs.getJobStore(), acquiresFailed);
Thread.sleep(delay);
} catch (Exception ignore) {
}
}
int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();
if(availThreadCount > 0) { // will always be true, due to semantics of blockForAvailableThreads...
List<OperableTrigger> triggers;
long now = System.currentTimeMillis();
clearSignaledSchedulingChange();
try {
//從QuartzSchedulerResources中取出triggers
triggers = qsRsrcs.getJobStore().acquireNextTriggers(
now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow());
acquiresFailed = 0;
if (log.isDebugEnabled())
log.debug("batch acquisition of " + (triggers == null ? 0 : triggers.size()) + " triggers");
} catch (JobPersistenceException jpe) {
if (acquiresFailed == 0) {
qs.notifySchedulerListenersError(
"An error occurred while scanning for the next triggers to fire.",
jpe);
}
if (acquiresFailed < Integer.MAX_VALUE)
acquiresFailed++;
continue;
} catch (RuntimeException e) {
if (acquiresFailed == 0) {
getLog().error("quartzSchedulerThreadLoop: RuntimeException "
+e.getMessage(), e);
}
if (acquiresFailed < Integer.MAX_VALUE)
acquiresFailed++;
continue;
}
if (triggers != null && !triggers.isEmpty()) {
now = System.currentTimeMillis();
long triggerTime = triggers.get(0).getNextFireTime().getTime();
long timeUntilTrigger = triggerTime - now;
while(timeUntilTrigger > 2) {
synchronized (sigLock) {
if (halted.get()) {
break;
}
if (!isCandidateNewTimeEarlierWithinReason(triggerTime, false)) {
try {
// we could have blocked a long while
// on 'synchronize', so we must recompute
now = System.currentTimeMillis();
timeUntilTrigger = triggerTime - now;
if(timeUntilTrigger >= 1)
sigLock.wait(timeUntilTrigger);
} catch (InterruptedException ignore) {
}
}
}
if(releaseIfScheduleChangedSignificantly(triggers, triggerTime)) {
break;
}
now = System.currentTimeMillis();
timeUntilTrigger = triggerTime - now;
}
// this happens if releaseIfScheduleChangedSignificantly decided to release triggers
if(triggers.isEmpty())
continue;
// set triggers to 'executing'
List<TriggerFiredResult> bndles = new ArrayList<TriggerFiredResult>();
boolean goAhead = true;
synchronized(sigLock) {
goAhead = !halted.get();
}
if(goAhead) {
try {
//根據到達指定時間的trigger從中取出job
List<TriggerFiredResult> res = qsRsrcs.getJobStore().triggersFired(triggers);
if(res != null)
bndles = res;
} catch (SchedulerException se) {
qs.notifySchedulerListenersError(
"An error occurred while firing triggers '"
+ triggers + "'", se);
//QTZ-179 : a problem occurred interacting with the triggers from the db
//we release them and loop again
for (int i = 0; i < triggers.size(); i++) {
qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
}
continue;
}
}
for (int i = 0; i < bndles.size(); i++) {
TriggerFiredResult result = bndles.get(i);
TriggerFiredBundle bndle = result.getTriggerFiredBundle();
Exception exception = result.getException();
if (exception instanceof RuntimeException) {
getLog().error("RuntimeException while firing trigger " + triggers.get(i), exception);
qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
continue;
}
// it's possible to get 'null' if the triggers was paused,
// blocked, or other similar occurrences that prevent it being
// fired at this time... or if the scheduler was shutdown (halted)
if (bndle == null) {
qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
continue;
}
JobRunShell shell = null;
try {
shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle);
shell.initialize(qs);
} catch (SchedulerException se) {
qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
continue;
}
//把任務放入執行緒池中執行
if (qsRsrcs.getThreadPool().runInThread(shell) == false) {
// this case should never happen, as it is indicative of the
// scheduler being shutdown or a bug in the thread pool or
// a thread pool being used concurrently - which the docs
// say not to do...
getLog().error("ThreadPool.runInThread() return false!");
qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
}
}
continue; // while (!halted)
}
} else { // if(availThreadCount > 0)
// should never happen, if threadPool.blockForAvailableThreads() follows contract
continue; // while (!halted)
}
long now = System.currentTimeMillis();
long waitTime = now + getRandomizedIdleWaitTime();
long timeUntilContinue = waitTime - now;
synchronized(sigLock) {
try {
if(!halted.get()) {
// QTZ-336 A job might have been completed in the mean time and we might have
// missed the scheduled changed signal by not waiting for the notify() yet
// Check that before waiting for too long in case this very job needs to be
// scheduled very soon
if (!isScheduleChanged()) {
sigLock.wait(timeUntilContinue);
}
}
} catch (InterruptedException ignore) {
}
}
} catch(RuntimeException re) {
getLog().error("Runtime error occurred in main trigger firing loop.", re);
}
} // while (!halted)
// drop references to scheduler stuff to aid garbage collection...
qs = null;
qsRsrcs = null;
}
相關文章
- gulp原始碼解析之任務管理原始碼
- gulp原始碼解析(三)—— 任務管理原始碼
- LiteOS-任務篇-原始碼分析-任務排程函式原始碼函式
- MapReduce——客戶端提交任務原始碼分析客戶端原始碼
- LiteOS核心原始碼分析:任務LOS_Schedule原始碼
- OkHttp3原始碼分析[任務佇列]HTTP原始碼佇列
- OkHttp 3.7原始碼分析(三)——任務佇列HTTP原始碼佇列
- Spark on Yarn 任務提交流程原始碼分析SparkYarn原始碼
- OkHttp3.7原始碼分析(三)——任務佇列HTTP原始碼佇列
- 比特幣原始碼分析:任務排程器的使用比特幣原始碼
- mybaits原始碼分析--事務管理(八)AI原始碼
- Spark3.0YarnCluster模式任務提交流程原始碼分析SparkYarn模式原始碼
- spring事務管理原始碼分析(二)事務處理流程分析Spring原始碼
- OkHttpClient原始碼分析(一)—— 同步、非同步請求分析和Dispatcher的任務排程HTTPclient原始碼非同步
- 懸賞任務系統原始碼_任務接單平臺原始碼PHP版附app原始碼PHPAPP
- LiteOS-任務篇-原始碼分析-系統啟動函式原始碼函式
- Spring原始碼分析(八)深入瞭解事務管理的流程Spring原始碼
- 需求分析的任務
- [原始碼分析] 分散式任務佇列 Celery 之 傳送Task & AMQP原始碼分散式佇列MQ
- [原始碼分析] 定時任務排程框架 Quartz 之 故障切換原始碼框架quartz
- 專案管理系列---任務管理工具深度分析專案管理
- 教育原始碼的重要任務——做好直播互動原始碼
- spark原始碼之任務提交過程Spark原始碼
- http服務原始碼分析HTTP原始碼
- http 服務原始碼分析HTTP原始碼
- spark core原始碼分析1 叢集啟動及任務提交過程Spark原始碼
- 小米開原始檔管理器MiCodeFileExplorer-原始碼研究(5)-AsyncTask非同步任務原始碼非同步
- spark核心(下)——job任務提交原始碼解析Spark原始碼
- [原始碼分析] 並行分散式任務佇列 Celery 之 Timer & Heartbeat原始碼並行分散式佇列
- quartz執行緒管理的原始碼分析quartz執行緒原始碼
- spring事務管理原始碼分析(一)配置和事務增強代理的生成流程Spring原始碼
- 定時任務管理
- Node.js 任務佇列Bull的原始碼淺析Node.js佇列原始碼
- 直播短影片原始碼,延遲任務的解決方法原始碼
- 【spring原始碼學習】spring的事務管理的原始碼解析Spring原始碼
- [原始碼分析] 分散式任務佇列 Celery 多執行緒模型 之 子程式原始碼分散式佇列執行緒模型
- Spring事務原始碼分析專題(一)JdbcTemplate使用及原始碼分析Spring原始碼JDBC
- 做好任務管理,從繁雜的任務中解脫出來