quartz源码解析(一)
本文的起因源于一次quartz的异常,在win2003正常运行的程序放在linux环境就抛出异常了,虽然找出异常没花我多长时间,不过由此加深了对quzrtz的了解;古人说,三折肱,为良医,说明经验对于我们平时开发的重要。
quartz是一个任务调度框架,对于开发者而言通常是透明的,如果不熟悉内部机制,碰到问题往往会束手无策;接下来本人本着开放的精神,来阐述本人对quartz的理解。
本人是采用spring对quartz封装的实现,spring的org.springframework.scheduling.quartz.SchedulerFactoryBean类用于初始化Scheduler对象并启动Scheduler对象主线程(通过实现spring的InitializingBean接口和SmartLifecycle接口)
Scheduler对象的初始化在SchedulerFactoryBean的void afterPropertiesSet()方法
步骤:
1 创建SchedulerFactory对象并初始化
2 通过第一步创建Scheduler工厂对象创建scheduler对象并初始化
3 添加配置文件中的相关监听器和触发器等
//---------------------------------------------------------------------
// Implementation of InitializingBean interface
//--------------------------------------------------------------------- public void afterPropertiesSet() throws Exception {
//这里省略部分代码
// Create SchedulerFactory instance.
SchedulerFactory schedulerFactory = (SchedulerFactory)
BeanUtils.instantiateClass(this.schedulerFactoryClass);
//初始化配置属性
initSchedulerFactory(schedulerFactory); //这里省略部分代码 // Get Scheduler instance from SchedulerFactory.
try {
//实例化scheduler对象
this.scheduler = createScheduler(schedulerFactory, this.schedulerName);
//初始化scheduler对象的上下文
populateSchedulerContext(); //这里省略部分代码
} finally {
//释放资源
} registerListeners();
registerJobsAndTriggers();
}
步骤一用于创建和初始化Scheduler工厂(SchedulerFactory这里默认为Class<?> schedulerFactoryClass = StdSchedulerFactory.class)
initSchedulerFactory(schedulerFactory)方法用于初始化StdSchedulerFactory的配置属性(这些属性用于下一步创建Scheduler对象)
/**
* 初始化配置属性
* Load and/or apply Quartz properties to the given SchedulerFactory.
* @param schedulerFactory the SchedulerFactory to initialize
*/
private void initSchedulerFactory(SchedulerFactory schedulerFactory)
throws SchedulerException, IOException { //这里省略部分代码 Properties mergedProps = new Properties(); if (this.resourceLoader != null) {
mergedProps.setProperty(StdSchedulerFactory.PROP_SCHED_CLASS_LOAD_HELPER_CLASS,
ResourceLoaderClassLoadHelper.class.getName());
} if (this.taskExecutor != null) {
mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,
LocalTaskExecutorThreadPool.class.getName());
}
else {
// Set necessary default properties here, as Quartz will not apply
// its default configuration when explicitly given properties.
mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName());
mergedProps.setProperty(PROP_THREAD_COUNT, Integer.toString(DEFAULT_THREAD_COUNT));
} if (this.configLocation != null) {
if (logger.isInfoEnabled()) {
logger.info("Loading Quartz config from [" + this.configLocation + "]");
}
PropertiesLoaderUtils.fillProperties(mergedProps, this.configLocation);
} CollectionUtils.mergePropertiesIntoMap(this.quartzProperties, mergedProps); if (this.dataSource != null) {
mergedProps.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, LocalDataSourceJobStore.class.getName());
} // Make sure to set the scheduler name as configured in the Spring configuration.
if (this.schedulerName != null) {
mergedProps.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, this.schedulerName);
} ((StdSchedulerFactory) schedulerFactory).initialize(mergedProps);
}
步骤二用于创建scheduler对象并初始化,其中创建scheduler对象方法如下
protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName)
throws SchedulerException { // Override thread context ClassLoader to work around naive Quartz ClassLoadHelper loading.
Thread currentThread = Thread.currentThread();
ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
boolean overrideClassLoader = (this.resourceLoader != null &&
!this.resourceLoader.getClassLoader().equals(threadContextClassLoader));
if (overrideClassLoader) {
currentThread.setContextClassLoader(this.resourceLoader.getClassLoader());
}
try {
SchedulerRepository repository = SchedulerRepository.getInstance();
synchronized (repository) {
Scheduler existingScheduler = (schedulerName != null ? repository.lookup(schedulerName) : null);
Scheduler newScheduler = schedulerFactory.getScheduler();
if (newScheduler == existingScheduler) {
throw new IllegalStateException("Active Scheduler of name '" + schedulerName + "' already registered " +
"in Quartz SchedulerRepository. Cannot create a new Spring-managed Scheduler of the same name!");
}
if (!this.exposeSchedulerInRepository) {
// Need to remove it in this case, since Quartz shares the Scheduler instance by default!
SchedulerRepository.getInstance().remove(newScheduler.getSchedulerName());
}
return newScheduler;
}
}
finally {
if (overrideClassLoader) {
// Reset original thread context ClassLoader.
currentThread.setContextClassLoader(threadContextClassLoader);
}
}
}
该方法里面首先设置当前线程的classload对象,然后调用schedulerFactory.工厂实例化scheduler对象
/**
* <p>
* Returns a handle to the Scheduler produced by this factory.
* </p>
*
* <p>
* If one of the <code>initialize</code> methods has not be previously
* called, then the default (no-arg) <code>initialize()</code> method
* will be called by this method.
* </p>
*/
public Scheduler getScheduler() throws SchedulerException {
if (cfg == null) {
initialize();
} SchedulerRepository schedRep = SchedulerRepository.getInstance(); Scheduler sched = schedRep.lookup(getSchedulerName()); if (sched != null) {
if (sched.isShutdown()) {
schedRep.remove(getSchedulerName());
} else {
return sched;
}
} sched = instantiate(); return sched;
}
方法Scheduler instantiate()实例化Scheduler对象,这个方法很长很长,其中关键代码如下
// Get ThreadPool Properties
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//实例化线程池对象
String tpClass = cfg.getStringProperty(PROP_THREAD_POOL_CLASS, null); if (tpClass == null) {
initException = new SchedulerException(
"ThreadPool class not specified. ",
SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
} try {
tp = (ThreadPool) loadHelper.loadClass(tpClass).newInstance();
} catch (Exception e) {
initException = new SchedulerException("ThreadPool class '"
+ tpClass + "' could not be instantiated.", e);
initException
.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
}
tProps = cfg.getPropertyGroup(PROP_THREAD_POOL_PREFIX, true);
try {
setBeanProps(tp, tProps);
} catch (Exception e) {
initException = new SchedulerException("ThreadPool class '"
+ tpClass + "' props could not be configured.", e);
initException
.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
} // Get JobStore Properties
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//实例化JobStore对象
String jsClass = cfg.getStringProperty(PROP_JOB_STORE_CLASS,
RAMJobStore.class.getName()); if (jsClass == null) {
initException = new SchedulerException(
"JobStore class not specified. ",
SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
} try {
js = (JobStore) loadHelper.loadClass(jsClass).newInstance();
} catch (Exception e) {
initException = new SchedulerException("JobStore class '" + jsClass
+ "' could not be instantiated.", e);
initException
.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
} SchedulerDetailsSetter.setDetails(js, schedName, schedInstId); tProps = cfg.getPropertyGroup(PROP_JOB_STORE_PREFIX, true, new String[] {PROP_JOB_STORE_LOCK_HANDLER_PREFIX});
try {
setBeanProps(js, tProps);
} catch (Exception e) {
initException = new SchedulerException("JobStore class '" + jsClass
+ "' props could not be configured.", e);
initException
.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
throw initException;
}
//初始化DBConnectionManager dbMgr try {
PoolingConnectionProvider cp = new PoolingConnectionProvider(
dsDriver, dsURL, dsUser, dsPass, dsCnt,
dsValidation);
dbMgr = DBConnectionManager.getInstance();
dbMgr.addConnectionProvider(dsNames[i], cp);
} catch (SQLException sqle) {
initException = new SchedulerException(
"Could not initialize DataSource: " + dsNames[i],
sqle);
throw initException;
} //创建QuartzSchedulerResources rsrcs对象
QuartzSchedulerResources rsrcs = new QuartzSchedulerResources();
rsrcs.setName(schedName);
rsrcs.setThreadName(threadName);
rsrcs.setInstanceId(schedInstId);
//设置JobRunShellFactory
rsrcs.setJobRunShellFactory(jrsf);
rsrcs.setMakeSchedulerThreadDaemon(makeSchedulerThreadDaemon);
rsrcs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
rsrcs.setRunUpdateCheck(!skipUpdateCheck);
rsrcs.setInterruptJobsOnShutdown(interruptJobsOnShutdown);
rsrcs.setInterruptJobsOnShutdownWithWait(interruptJobsOnShutdownWithWait);
rsrcs.setJMXExport(jmxExport);
rsrcs.setJMXObjectName(jmxObjectName); SchedulerDetailsSetter.setDetails(tp, schedName, schedInstId);
//设置ThreadPool
rsrcs.setThreadPool(tp);
//创建并启动工作线程
tp.initialize();
tpInited = true;
//设置JobStore
rsrcs.setJobStore(js); schedCtxt = new SchedulingContext();
schedCtxt.setInstanceId(rsrcs.getInstanceId()); qs = new QuartzScheduler(rsrcs, schedCtxt, idleWaitTime, dbFailureRetry);
qsInited = true; // Create Scheduler ref...
Scheduler scheduler = instantiate(rsrcs, qs); // fire up job store, and runshell factory js.setInstanceId(schedInstId);
js.setInstanceName(schedName);
js.initialize(loadHelper, qs.getSchedulerSignaler()); jrsf.initialize(scheduler, schedCtxt); qs.initialize(); // prevents the repository from being garbage collected
qs.addNoGCObject(schedRep);
// prevents the db manager from being garbage collected
if (dbMgr != null) {
qs.addNoGCObject(dbMgr);
} schedRep.bind(scheduler);
该方法最终通过调用Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs)方法创建对象
protected Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs) {
SchedulingContext schedCtxt = new SchedulingContext();
schedCtxt.setInstanceId(rsrcs.getInstanceId()); Scheduler scheduler = new StdScheduler(qs, schedCtxt);
return scheduler;
}
通过StdScheduler的构造函数传入QuartzScheduler sched, SchedulingContext schedCtxt参数对象
这里关键是QuartzScheduler sched参数对象是作为StdScheduler对象的代理身份的,我们调用的StdScheduler对象方法都是间接执行QuartzScheduler sched的相关方法
(修正:StdScheduler对象为QuartzScheduler sched参数对象代理,QuartzScheduler sched参数对象为真实对象)
QuartzScheduler对象的构造方法QuartzSchedulerResources resources参数保存了相关资源的引用,具体QuartzScheduler对象具体逻辑下文再分析
现在回到SchedulerFactoryBean的void afterPropertiesSet()方法,populateSchedulerContext()用于初始化scheduler对象的上下文,这里是保存相关对象的引用,在任务执行方法里面方便调用相关引用对象的方法
/**
* Expose the specified context attributes and/or the current
* ApplicationContext in the Quartz SchedulerContext.
*/
private void populateSchedulerContext() throws SchedulerException {
// Put specified objects into Scheduler context.
if (this.schedulerContextMap != null) {
this.scheduler.getContext().putAll(this.schedulerContextMap);
} // Register ApplicationContext in Scheduler context.
if (this.applicationContextSchedulerContextKey != null) {
if (this.applicationContext == null) {
throw new IllegalStateException(
"SchedulerFactoryBean needs to be set up in an ApplicationContext " +
"to be able to handle an 'applicationContextSchedulerContextKey'");
}
this.scheduler.getContext().put(this.applicationContextSchedulerContextKey, this.applicationContext);
}
}
步骤三添加监听器和触发器,这里不具体分析在创建创建Scheduler对象后,接下来就是调用它的启动方法(主线程方法)了
/**
* Start the Quartz Scheduler, respecting the "startupDelay" setting.
* @param scheduler the Scheduler to start
* @param startupDelay the number of seconds to wait before starting
* the Scheduler asynchronously
*/
protected void startScheduler(final Scheduler scheduler, final int startupDelay) throws SchedulerException {
if (startupDelay <= 0) {
logger.info("Starting Quartz Scheduler now");
scheduler.start();
}
else {
if (logger.isInfoEnabled()) {
logger.info("Will start Quartz Scheduler [" + scheduler.getSchedulerName() +
"] in " + startupDelay + " seconds");
}
Thread schedulerThread = new Thread() {
@Override
public void run() {
try {
Thread.sleep(startupDelay * 1000);
}
catch (InterruptedException ex) {
// simply proceed
}
if (logger.isInfoEnabled()) {
logger.info("Starting Quartz Scheduler now, after delay of " + startupDelay + " seconds");
}
try {
scheduler.start();
}
catch (SchedulerException ex) {
throw new SchedulingException("Could not start Quartz Scheduler after delay", ex);
}
}
};
schedulerThread.setName("Quartz Scheduler [" + scheduler.getSchedulerName() + "]");
schedulerThread.setDaemon(true);
schedulerThread.start();
}
}
首先是延迟指定时间,然后在线程对象里面调用主线程方法
---------------------------------------------------------------------------
本系列quartz源码解析系本人原创
作者 博客园 刺猬的温驯
邮箱 chenying998179(爬虫绕道)163.com
本文链接 http://www.cnblogs.com/chenying99/p/3151850.html
本文版权归作者所有,未经作者同意,严禁转载及用作商业传播,否则将追究法律责任。
quartz源码解析(一)的更多相关文章
- quartz源码解析--转
quartz源码解析(一) . http://ssuupv.blog.163.com/blog//146156722013829111028966/ 任何个人.任何企业.任何行业都会有作业调度的需求 ...
- springboot源码解析-管中窥豹系列之自动装配(九)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- [源码解析] 并行分布式任务队列 Celery 之 负载均衡
[源码解析] 并行分布式任务队列 Celery 之 负载均衡 目录 [源码解析] 并行分布式任务队列 Celery 之 负载均衡 0x00 摘要 0x01 负载均衡 1.1 哪几个 queue 1.1 ...
- [源码解析] 并行分布式框架 Celery 之 容错机制
[源码解析] 并行分布式框架 Celery 之 容错机制 目录 [源码解析] 并行分布式框架 Celery 之 容错机制 0x00 摘要 0x01 概述 1.1 错误种类 1.2 失败维度 1.3 应 ...
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
- jQuery2.x源码解析(缓存篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...
随机推荐
- UVa 10780 (质因数分解) Again Prime? No Time.
求mk整除n!,求k的最大值. 现将m分解质因数,比如对于素数p1分解出来的指数为k1,那么n!中能分解出多少个p1出来呢? 考虑10!中2的个数c:1~10中有10/2个数是2的倍数,c += 5: ...
- UVa 11859 (Nim) Division Game
把每一行m个数所有的素因子看做一堆,就把问题转化为n堆的Nim游戏. 然后预处理一下10000以内每个数素因数的个数,再根据书上的Bouton定理,计算一下n行素因数个数的异或和. 为0是先手必败局面 ...
- GridView CommandArgument 绑定多个参数
我们在使用GridView的时候 有时会需要绑定多个参数 <asp:GridView ID="gvwVoxListAll" runat="server" ...
- ES PES TS
1.流媒体系统结构 ES:elemental stream 基本数据流: PES:packet elemental stream分组的基本数据流: 然后把PES打包成PS ,TS流,PS:progra ...
- 持有对象:总结JAVA中的常用容器和迭代器,随机数 速查
JAVA使用术语“Collection”来指代那些表示集合的对象,JAVA提供的接口很多,首先我们先来记住他们的层次结构: java集合框架的基本接口/类层次结构 java.util.Collecti ...
- java/python中获取当前系统时间,并与字符串相互转换格式,或者转化成秒数,天数等整数
java转换成秒数 Date类有一个getTime()可以换回秒数,例如: public class DateToSecond { public static void main(String[] a ...
- iOS开发:AFNetworking、MKNetworkKit和ASIHTTPRequest比较
转:http://www.xue5.com/Mobile/iOS/747036.html 之前一直在使用ASIHTTPRequest作为网络库,但是由于其停止更新,iOS7上可能出现更多的问题,于是决 ...
- 【c++内存分布系列】单继承
父类包括成员函数.静态函数.静态方法,子类包括成员函数.静态函数.静态方法的情况与一个类时完全一致,这里就不做分析了.子类单独包含虚函数时继承无关,也不做分析了. 一.父类子类都为空 #include ...
- HDU 5858 Hard problem
Hard problem Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Tota ...
- IOS AsyncSocket
导入AsyncSocket.h AsyncSocket.m AsyncUdpSocket.h AsyncUdpSocket.m 以及 CFNetWork.framework async ...