zoukankan      html  css  js  c++  java
  • 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

    本文版权归作者所有,未经作者同意,严禁转载及用作商业传播,否则将追究法律责任。

  • 相关阅读:
    POJ 1860 Currency Exchange (Bellman ford)
    POJ 1502 MPI Maelstrom (最短路)
    2015 Multi-University Training Contest 2 1006(DFS)
    HDU 1495 非常可乐(枚举+DFS)
    HDU 5289 Assignment(单调队列)
    ACDream 1734 Can you make a water problem?(贪心)
    ACDream 1735 输油管道
    ACDream 1726 A Math game (折半查找)
    CSU 1602 Needle Throwing Game (投针问题)
    CSU 1604 SunnyPig (BFS)
  • 原文地址:https://www.cnblogs.com/chenying99/p/3151850.html
Copyright © 2011-2022 走看看