scheduler.start()是Quartz的启动方式!下面进行分析,方便自己查看!

我都是分析的jobStore 方式为jdbc的SimpleTrigger!RAM的方式类似分析方式!

Quartz学习——QuartzSchedulerThread.run 源码分析:http://blog.csdn.net/u010648555/article/details/53525041


解释:

{0} : 表的前缀 ,如表qrtz_trigger ,{0}== qrtz_

{1} :quartz.properties 中配置的 org.quartz.scheduler.instanceName: myInstanceName ,{1} ==myInstanceName


Quartz 的启动要调用start()方法进行线程的启动,并执行需要出发的Trigger,start方法里面进行的操作:

  1. 启动的初始化
  2. 判断是否集群,对应不同的操作
  3. 若是非集群,首先有恢复机制,恢复任何失败或misfire的作业,并根据需要清理数据存储。
  4. 初始化线程管理,唤醒所有等待的线程!

下面就是简单的源码分析:

1.QuartzScheduler.start();

public void start() throws SchedulerException {

        if (shuttingDown|| closed) {
throw new SchedulerException(
"The Scheduler cannot be restarted after shutdown() has been called.");
} // QTZ-212 : calling new schedulerStarting() method on the listeners
// right after entering start()
notifySchedulerListenersStarting(); if (initialStart == null) {//初始化标识为null,进行初始化操作
initialStart = new Date();
this.resources.getJobStore().schedulerStarted();//1.1 主要分析的地方
startPlugins();
} else { resources.getJobStore().schedulerResumed();//1.2如果已经初始化过,则恢复jobStore
} schedThread.togglePause(false);//3.1 唤醒所有等待的线程 getLog().info(
"Scheduler " + resources.getUniqueIdentifier() + " started."); notifySchedulerListenersStarted();
}

1.1 this.resources.getJobStore().schedulerStarted();//主要分析的地方

public void schedulerStarted() throws SchedulerException {
//是集群
if (isClustered()) {
clusterManagementThread = new ClusterManager();
if(initializersLoader != null)
clusterManagementThread.setContextClassLoader(initializersLoader);
clusterManagementThread.initialize();
} else {//不是集群
try {
recoverJobs();//2.1 恢复job
} catch (SchedulerException se) {
throw new SchedulerConfigException(
"Failure occured during job recovery.", se);
}
} misfireHandler = new MisfireHandler();
if(initializersLoader != null)
misfireHandler.setContextClassLoader(initializersLoader);
misfireHandler.initialize();//2.2 获取ThreadExecutor 线程管理
schedulerRunning = true; getLog().debug("JobStore background threads started (as scheduler was started).");
}

1.2如果已经初始化过,则恢复调度器运行 resources.getJobStore().schedulerResumed();

 private volatile boolean schedulerRunning = false;//默认schedulerRunning = false
public void schedulerResumed() {
schedulerRunning = true;
}

2.1 恢复job recoverJobs();

//启动的时候 有一个恢复机制:
//recoverJobs ----- 将恢复任何失败或misfire的作业,并根据需要清理数据存储。
protected void recoverJobs() throws JobPersistenceException {
executeInNonManagedTXLock(
LOCK_TRIGGER_ACCESS,
new VoidTransactionCallback() {
public void executeVoid(Connection conn) throws JobPersistenceException {
recoverJobs(conn);//恢复job
}
}, null);
} protected void recoverJobs(Connection conn) throws JobPersistenceException {
try {
(1)//更新不一致的作业状态 先修改状态,将 ACQUIRED 和 BLOCKED ---> WAITING
int rows = getDelegate().updateTriggerStatesFromOtherStates(conn,
STATE_WAITING, STATE_ACQUIRED, STATE_BLOCKED); rows += getDelegate().updateTriggerStatesFromOtherStates(conn,
STATE_PAUSED, STATE_PAUSED_BLOCKED, STATE_PAUSED_BLOCKED); //----更新sql---
//"UPDATE {0}TRIGGERS SET TRIGGER_STATE = ? WHERE SCHED_NAME = {1} AND (TRIGGER_STATE = ? OR TRIGGER_STATE = ?)" getLog().info(
"Freed " + rows
+ " triggers from 'acquired' / 'blocked' state."); // clean up misfired jobs
//2.1.1 清理misfire的jobs
recoverMisfiredJobs(conn, true); // recover jobs marked for recovery that were not fully executed
//2.1.2 恢复未完全执行的标记为恢复的作业 --查询 qrtz_fire_trigger
List<OperableTrigger> recoveringJobTriggers = getDelegate()
.selectTriggersForRecoveringJobs(conn);
getLog()
.info(
"Recovering "
+ recoveringJobTriggers.size()
+ " jobs that were in-progress at the time of the last shut-down."); for (OperableTrigger recoveringJobTrigger: recoveringJobTriggers) {
if (jobExists(conn, recoveringJobTrigger.getJobKey())) {
recoveringJobTrigger.computeFirstFireTime(null);
storeTrigger(conn, recoveringJobTrigger, null, false,
STATE_WAITING, false, true);
}
}
getLog().info("Recovery complete."); // remove lingering 'complete' triggers...
//2.1.3 移除state == complete
List<TriggerKey> cts = getDelegate().selectTriggersInState(conn, STATE_COMPLETE);
for(TriggerKey ct: cts) {
removeTrigger(conn, ct);
}
getLog().info(
"Removed " + cts.size() + " 'complete' triggers."); // clean up any fired trigger entries
//2.1.4 清理任何已触发的触发器条目
int n = getDelegate().deleteFiredTriggers(conn);
getLog().info("Removed " + n + " stale fired job entries.");
} catch (JobPersistenceException e) {
throw e;
} catch (Exception e) {
throw new JobPersistenceException("Couldn't recover jobs: "
+ e.getMessage(), e);
}
}

2.1.1 清理misfire的jobs recoverMisfiredJobs(conn, true);

	//是否有misfire的Trigger
//我们必须仍然寻找MISFIRED状态,以防触发器被遗忘
//在此状态下升级到此版本不支持
(a1)hasMisfiredTriggersInState(conn, STATE_WAITING, getMisfireTime(),
maxMisfiresToHandleAtATime, misfiredTriggers);
////getMisfireTime() 当前时间 -(减去) 一分钟 ,maxMisfiresToHandleAtATime == -1 ,misfiredTriggers== null "SELECT TRIGGER_NAME, TRIGGER_GROUP FROM {0}TRIGGERS WHERE SCHED_NAME = {1} AND NOT (MISFIRE_INSTR = -1) AND NEXT_FIRE_TIME < ? AND TRIGGER_STATE = ? ORDER BY NEXT_FIRE_TIME ASC, PRIORITY DESC" 上面sql查询出来结果是个list
(aa1)若resultList.size() == count 返回 TRUE!! 否则 返回false!
(aa2)不等于 count ,封装数据,到resultList中,triggername TriggerGroup //查询出来有misfire 的 Trigger
(b2) misfiredTriggers.size() > 0
(bb1)输出日志信息 :getLog().info(
"Handling " + misfiredTriggers.size() +
" trigger(s) that missed their scheduled fire-time."); (bb2)循环 misfiredTriggers List集合
for (TriggerKey triggerKey: misfiredTriggers) {
//retrieveTrigger ,检索Trigger,检索到进行数据封装
OperableTrigger trig =
retrieveTrigger(conn, triggerKey);
//retrieveTrigger 执行的操作
(1)"SELECT * FROM {0}TRIGGERS WHERE SCHED_NAME = {1} AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?"
(2)关联Trigger对应的类型,如simpleTrigger等 if (trig == null) {
continue;
} //do 更新misfire的触发器
doUpdateOfMisfiredTrigger(conn, trig, false, STATE_WAITING, recovering); //recovering===TRUE
(1)cal = retrieveCalendar(conn, trig.getCalendarName()); 搞这个表,qrtz_calendar
(2)trig.updateAfterMisfire(cal); //simpleTrigger默认的misfire 机制
setNextFireTime(new Date()); //设置下次执行的时间(next_fire_time)为当前时间!这里比较重要!!!
(3) getNextFireTime != null
if (trig.getNextFireTime() == null) {
storeTrigger(conn, trig,
null, true, STATE_COMPLETE, forceState, recovering);
schedSignaler.notifySchedulerListenersFinalized(trig);
} else {
storeTrigger(conn, trig, null, true, newStateIfNotComplete,
forceState, false); // job == null replaceExisting ==true state==waitting forceState==false recovering==false
storeTrigger(Connection conn,
OperableTrigger newTrigger, JobDetail job, boolean replaceExisting, String state,
boolean forceState, boolean recovering) //Insert or update a trigger.
boolean existingTrigger = triggerExists(conn, newTrigger.getKey());
if (existingTrigger) { //state == waitting
getDelegate().updateTrigger(conn, newTrigger, state, job); //更新sql
/*
"UPDATE {0}TRIGGERS SET JOB_NAME = ?, JOB_GROUP = ?, DESCRIPTION = ?, NEXT_FIRE_TIME = ?, PREV_FIRE_TIME = ?, TRIGGER_STATE = ?, TRIGGER_TYPE = ?, START_TIME = ?, END_TIME = ?, CALENDAR_NAME = ?, MISFIRE_INSTR = ?, PRIORITY = ? WHERE SCHED_NAME = {1} AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?"*/ } else {
getDelegate().insertTrigger(conn, newTrigger, state, job);
//插入sql
/*"INSERT INTO {0}TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, JOB_NAME, JOB_GROUP, DESCRIPTION, NEXT_FIRE_TIME, PREV_FIRE_TIME, TRIGGER_STATE, TRIGGER_TYPE, START_TIME, END_TIME, CALENDAR_NAME, MISFIRE_INSTR, JOB_DATA, PRIORITY) VALUES({1}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"*/
}
} (c3) long earliestNewTime = Long.MAX_VALUE; // long earliestNewTime = Long.MAX_VALUE; === 9223372036854775807
if(trig.getNextFireTime() != null && trig.getNextFireTime().getTime() < earliestNewTime){
earliestNewTime = trig.getNextFireTime().getTime();
}

2.1.2 恢复未完全执行的标记为恢复的作业

List<OperableTrigger> recoveringJobTriggers = getDelegate()
.selectTriggersForRecoveringJobs(conn);
// INSTANCE_NAME == dufy_test REQUESTS_RECOVERY == true 实际封装到数据库查询是 REQUESTS_RECOVERY== 1
"SELECT * FROM {0}FIRED_TRIGGERS WHERE SCHED_NAME = {1} AND INSTANCE_NAME = ? AND REQUESTS_RECOVERY = ?"
//具体怎么是 true是怎么转换成为 1的见附1图片! Recovery complete.恢复完成!!

2.1.3 移除state == complete

List<TriggerKey> cts = getDelegate().selectTriggersInState(conn, STATE_COMPLETE);
-----------------------------------------------------------------------------
"SELECT TRIGGER_NAME, TRIGGER_GROUP FROM {0}TRIGGERS WHERE SCHED_NAME = {1} AND TRIGGER_STATE = ?"
-----------------------------------------------------------------------------
for(TriggerKey ct: cts) {
removeTrigger(conn, ct);
---------------------------------------------------------------------
(a)删除前,先查询jobDetail
JobDetail job = getDelegate().selectJobForTrigger(conn,getClassLoadHelper(), key, false);
"SELECT J.JOB_NAME, J.JOB_GROUP, J.IS_DURABLE, J.JOB_CLASS_NAME, J.REQUESTS_RECOVERY FROM {0}TRIGGERS T, {0}JOB_DETAILS J WHERE T.SCHED_NAME = {1} AND J.SCHED_NAME = {1} AND T.TRIGGER_NAME = ? AND T.TRIGGER_GROUP = ? AND T.JOB_NAME = J.JOB_NAME AND T.JOB_GROUP = J.JOB_GROUP" (b)删除触发器,其侦听器及其Simple / Cron / BLOB子表条目。
boolean removedTrigger = deleteTriggerAndChildren(conn, key);
deleteTrigger(Connection conn, TriggerKey triggerKey)
(b1)deleteTriggerExtension
"DELETE FROM {0}SIMPLE_TRIGGERS WHERE SCHED_NAME = {1} AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?"
"DELETE FROM {0}BLOB_TRIGGERS WHERE SCHED_NAME = {1} AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?" (b2)"DELETE FROM {0}TRIGGERS WHERE SCHED_NAME = {1} AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?" (c)是否删除jobdetail ,判断 isDurable 默认 为false。
if (null != job && !job.isDurable()) {
int numTriggers = getDelegate().selectNumTriggersForJob(conn,
job.getKey());
---------------------------------------------------------
"SELECT COUNT(TRIGGER_NAME) FROM {0}TRIGGERS WHERE SCHED_NAME = {1} AND JOB_NAME = ? AND JOB_GROUP = ?"
---------------------------------------------------------
if (numTriggers == 0) {
// Don't call removeJob() because we don't want to check for
// triggers again.
//不要调用removeJob(),因为我们不想再次检查触发器。
deleteJobAndChildren(conn, job.getKey()); //删除作业及其侦听器。
-----------------------------------------------------
//deleteJobDetail(Connection conn, JobKey jobKey) 删除给定作业的作业明细记录。
"DELETE FROM {0}JOB_DETAILS WHERE SCHED_NAME = {1} AND JOB_NAME = ? AND JOB_GROUP = ?"
-----------------------------------------------------
}
}
}

2.1.4 清理任何已触发的触发器条目

 int n = getDelegate().deleteFiredTriggers(conn);
----------------------------------------------------------------------------
"DELETE FROM {0}FIRED_TRIGGERS WHERE SCHED_NAME = {1}"
----------------------------------------------------------------------------

2.2 获取ThreadExecutor 线程管理 misfireHandler.initialize();

public void initialize() {
ThreadExecutor executor = getThreadExecutor();
//getThreadExecutor == private ThreadExecutor threadExecutor = new DefaultThreadExecutor();
executor.execute(MisfireHandler.this); //启动线程执行 对应job的 execute方法
//MisfireHandler == class MisfireHandler extends Thread 继承了Thread
}

3.1 唤醒所有等待的线程 schedThread.togglePause(false);

 schedThread.togglePause(false);
//指示主处理循环在下一个可能的点暂停。
void togglePause(boolean pause) {
synchronized (sigLock) {
paused = pause; if (paused) {
signalSchedulingChange(0);
------------------------------------------
//发信号通知主要处理循环,已经进行了调度的改变 - 以便中断在等待misfire时间到达时可能发生的任何睡眠。
public void signalSchedulingChange(long candidateNewNextFireTime) {
synchronized(sigLock) {
signaled = true;
signaledNextFireTime = candidateNewNextFireTime;
sigLock.notifyAll(); // private final Object sigLock = new Object();
}
}
------------------------------------------
} else {
sigLock.notifyAll();//唤醒所有等待的线程
}
}
}

附1:true 是如何转换为 1 的:


欢迎访问我的csdn博客,我们一同成长!

"不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!"

博客首页:http://blog.csdn.net/u010648555

Quartz源码——scheduler.start()启动源码分析(二)的更多相关文章

  1. 分析easyswoole3.0源码,服务启动为例(二)

    以下内容需要结合es的源码,不然可能会觉得跳跃.先描述下es启动的大致流程.es启动的时候注册异常处理函数以及加载配置文件.根据位置文件的设置选择启动哪种swoole服务.然后用一个事件注册类,注册s ...

  2. Activity启动场景Task分析(二)

    场景分析 下面通过启动Activity的代码来分析一下: 1.桌面 首先,我们看下处于桌面时的状态,运行命令: adb shell dumpsys activity 结果如下 ACTIVITY MAN ...

  3. spring-boot-2.0.3之quartz集成,数据源问题,源码探究

    前言 开心一刻 着火了,他报警说:119吗,我家发生火灾了. 119问:在哪里? 他说:在我家. 119问:具体点. 他说:在我家的厨房里. 119问:我说你现在的位置. 他说:我趴在桌子底下. 11 ...

  4. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  5. Spark Streaming揭秘 Day25 StreamingContext和JobScheduler启动源码详解

    Spark Streaming揭秘 Day25 StreamingContext和JobScheduler启动源码详解 今天主要理一下StreamingContext的启动过程,其中最为重要的就是Jo ...

  6. Flume-ng源码解析之启动流程

    今天我们通过阅读Flume-NG的源码来看看Flume的整个启动流程,废话不多说,翠花,上源码!! 1 主类也是启动类 在这里我贴出Application中跟启动有关的方法,其他你们可以自己看源码,毕 ...

  7. 【MySQL源码】源码安装和启动mysql

    --[MySQL源码]源码安装和启动mysql --------------------------------------2014/08/19 本机环境:ubuntu12.04,fedora-17 ...

  8. Jfinal启动源码解读

    本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口  JF ...

  9. spring-boot-2.0.3启动源码篇一 - SpringApplication构造方法

    前言 spring-boot-2.0.3应用篇 - shiro集成,实现了spring-boot与shiro的整合,效果大家也看到了,工程确实集成了shiro的认证与授权功能.如果大家能正确搭建起来, ...

随机推荐

  1. Servlet中Response对象应用2(输出随机验证码图片)

    预期结果如图: 可用于登陆界面的验证 需要使用random类和绘画相关的几个类.以及imageio的内容. import java.awt.*; import java.awt.image.Buffe ...

  2. 迈向angularjs2系列(2):angular2指令详解

    一:angular2 helloworld! 为了简单快速的运行一个ng2的app,那么通过script引入预先编译好的angular2版本和页面的基本框架. index.html: <!DOC ...

  3. jquery的2.0.3版本源码系列(2):21行-94行定义了一些变量和函数 jQuery=function(){}

    2.1.bug通过索引查询 这里的#13335是bug的索引,如何查询呢? 第一步,浏览器地址栏输入"https://bugs.jquery.com/". 第二步,在网页的搜索框里 ...

  4. .NET Core 2.0 应用程序大小减少50%

    .NET Core 2.0减小体积瘦身官方工具 IL Linker. IL Linker 来源于mono的linker  https://github.com/mono/linker,目前还是预览版本 ...

  5. selenium实战学习第一课

    #-*- coding:utf-8 -*- __author__ = "carry" from selenium import webdriver from selenium.we ...

  6. jdk不同版本对String拼接的优化分析

    1. 测试demo代码 测试循环中字符串拼接优化 public class ForTest { public static void main(String[] args) { String a = ...

  7. 使用jquery.form.js提交表单上传文件

    方法: 1.formSerilize()  用于序列化表单中的数据,并将其自动整理成适合AJAX异步请求的URL地址格式. 2.clearForm()   清除表单中所有输入值的内容. 3.restF ...

  8. 深入剖析ConcurrentHashMap 一

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt201 ConcurrentHashMap是Java5中新增加的一个线程安全的 ...

  9. 第1阶段——uboot分析之查找命令run_command函数和命令定义过程(6)

    本节主要学习,run_command函数命令查找过程,命令生成过程 1.run_command函数命令查找过程分析:在u-boot界面中(main_loop();位于u-boot-1.1.6/comm ...

  10. 《深入浅出MySQL》之SQL基础

    SQL是Structure Query language(结构化查询语言)的缩写,它是使用关系模型的数据库应用语言.在众多开源数据中,MySQL正式其中最杰出的代表,MySQL是由三个瑞典人于20世纪 ...