Quartz与Spring集成——启动调度器

前言

《Quartz与Spring集成——创建调度器》一文中介绍了调度器的创建过程,本文将分析其启动过程。熟悉Spring原理的人都知道AbstractApplicationContext的refresh方法的重要性,在refresh方法中调用了finishRefresh方法,最后会调用到SchedulerFactoryBean的start方法,其调用栈如图1所示。


图1 SchedulerFactoryBean的start方法的调用栈

根据图1的内容,我们知道spring容器初始化完毕的最后会启动所有的bean,SchedulerFactoryBean的start方法就是这时候被调用的。

启动调度器

SchedulerFactoryBean的start方法的实现见代码清单1所示。

代码清单1

	@Override
	public void start() throws SchedulingException {
		if (this.scheduler != null) {
			try {
				startScheduler(this.scheduler, this.startupDelay);
			}
			catch (SchedulerException ex) {
				throw new SchedulingException("Could not start Quartz Scheduler", ex);
			}
		}
	}

startScheduler方法的实现,见代码清单2。

代码清单2

	protected void startScheduler(final Scheduler scheduler, final int startupDelay) throws SchedulerException {
		if (startupDelay <= 0) {
			logger.info("Starting Quartz Scheduler now");
			scheduler.start();
		}
		else {
		//此分支启动一个后台线程,sleep参数startupDelay指定的秒数后,再启动调度器(即调用scheduler.start()),主要用于延迟启动
		}
	}
startScheduler方法中的第二个条件分支用于延迟启动调度器,即当参数startupDelay大于0时,启动一个后台线程,睡眠(sleep)参数startupDelay指定的秒数后,再启动调度器(即调用scheduler.start();)。由于实际上也是执行scheduler.start();故此没有列出其代码。

以默认的Scheduler实现类StdScheduler为例,其start方法的实现如下:

    public void start() throws SchedulerException {
        sched.start();
    }
根据《Quartz与Spring集成——创建调度器》一文的内容我们知道,这里的sched实际是QuartzScheduler的实例,其start方法的实现见代码清单3。

代码清单3
    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) {
            initialStart = new Date();
            this.resources.getJobStore().schedulerStarted();            
            startPlugins();
        } else {
            resources.getJobStore().schedulerResumed();
        }

        schedThread.togglePause(false);

        getLog().info(
                "Scheduler " + resources.getUniqueIdentifier() + " started.");
        
        notifySchedulerListenersStarted();
    }

阅读代码清单3,整理启动QuartzScheduler的步骤如下:

  1. 启动前对关闭中(shuttingDown)、已关闭(closed)的状态校验,此时会抛出SchedulerException,避免进入不正确的状态;
  2. 通知所有对调度器监听的监听器——现在正在启动调度器,notifySchedulerListenersStarting方法的实现见代码清单4;
  3. 启动调度器,以LocalDataSourceJobStore为例,实际调用了其父类JobStoreSupport的schedulerStarted方法;
  4. 启动插件,startPlugins方法的实现见代码清单5;
  5. 调用QuartzSchedulerThread的togglePause方法(见代码清单6),其作用为在保证线程安全的前提下将paused设置为false,同时唤醒所有等待sigLock这个锁的线程。在《Quartz与Spring集成——创建调度器》一文中介绍QuartzSchedulerThread的启动的时候,曾经说过由于paused为true时导致不断轮询和等待sigLock。到这里QuartzSchedulerThread被唤醒后,run方法将挑出这个轮询继续执行,QuartzSchedulerThread的启动才真正开始;
  6. 通知所有对调度器监听的监听器——现在启动调度器已完成,notifySchedulerListenersStarted方法的实现见代码清单7;

代码清单4
    public void notifySchedulerListenersStarting() {
        // build a list of all scheduler listeners that are to be notified...
        List<SchedulerListener> schedListeners = buildSchedulerListenerList();

        // notify all scheduler listeners
        for (SchedulerListener sl : schedListeners) {
            try {
                sl.schedulerStarting();
            } catch (Exception e) {
                getLog().error(
                        "Error while notifying SchedulerListener of startup.",
                        e);
            }
        }
    }

代码清单5

    private void startPlugins() {
        java.util.Iterator<SchedulerPlugin> itr = resources.getSchedulerPlugins().iterator();
        while (itr.hasNext()) {
            SchedulerPlugin plugin = itr.next();
            plugin.start();
        }
    }
代码清单6
    void togglePause(boolean pause) {
        synchronized (sigLock) {
            paused = pause;

            if (paused) {
                signalSchedulingChange(0);
            } else {
                sigLock.notifyAll();
            }
        }
    }
代码清单7
    public void notifySchedulerListenersStarted() {
        // build a list of all scheduler listeners that are to be notified...
        List<SchedulerListener> schedListeners = buildSchedulerListenerList();

        // notify all scheduler listeners
        for(SchedulerListener sl: schedListeners) {
            try {
                sl.schedulerStarted();
            } catch (Exception e) {
                getLog().error(
                        "Error while notifying SchedulerListener of startup.",
                        e);
            }
        }
    }

启动JobStore

以LocalDataSourceJobStore为例,特别来分析下其schedulerStarted方法(见代码清单8)的实现,其处理步骤如下:

  1. 如果是集群部署,则创建集群管理器ClusterManager(直接继承了Thread,),并调用其initialize方法(见代码清单9)执行ClusterManager自身;
  2. 如果不是集群部署,则调用recoverJobs方法恢复任何失败的或者触发失常的作业;
  3. 创建MisfireHandler(也直接继承了Thread,用于恢复任何失败的或者触发失常的作业),并调用其initialize方法(见代码清单10)执行MisfireHandler自身;
代码清单8

    public void schedulerStarted() throws SchedulerException {

        if (isClustered()) {
            clusterManagementThread = new ClusterManager();
            if(initializersLoader != null)
                clusterManagementThread.setContextClassLoader(initializersLoader);
            clusterManagementThread.initialize();
        } else {
            try {
                recoverJobs();
            } catch (SchedulerException se) {
                throw new SchedulerConfigException(
                        "Failure occured during job recovery.", se);
            }
        }

        misfireHandler = new MisfireHandler();
        if(initializersLoader != null)
            misfireHandler.setContextClassLoader(initializersLoader);
        misfireHandler.initialize();
        schedulerRunning = true;
        
        getLog().debug("JobStore background threads started (as scheduler was started).");
    }

代码清单9

        public void initialize() {
            this.manage();

            ThreadExecutor executor = getThreadExecutor();
            executor.execute(ClusterManager.this);
        }
代码清单10

        public void initialize() {
            ThreadExecutor executor = getThreadExecutor();
            executor.execute(MisfireHandler.this);
        }


小结

经过以上分析,对Quartz如何启动调度器的原理有了较深入的了解。前面说过当paused设置为false,QuartzSchedulerThread才正式启动,有关QuartzSchedulerThread的正式启动请阅读《Quartz与Spring集成——QuartzSchedulerThread的执行分析》一文。


后记:个人总结整理的《深入理解Spark:核心思想与源码分析》一书现在已经正式出版上市,目前京东、当当、天猫等网站均有销售,欢迎感兴趣的同学购买。


京东:http://item.jd.com/11846120.html 

当当:http://product.dangdang.com/23838168.html 


发布了99 篇原创文章 · 获赞 293 · 访问量 87万+
展开阅读全文

Quartz定时器启动报错

06-16

在配置使用quartz时报错,找了很久不知道怎么解决,可能是jar包的问题还是什么问题呢? 错误信息如下: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'quartzScheduler' defined in class path resource [applicationContext-job.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.quartz.SchedulerContext.put(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1566) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.context.support.DefaultLifecycleProcessor.getLifecycleBeans(DefaultLifecycleProcessor.java:281) at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:131) at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:112) at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:770) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:483) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106) at org.mspring.platform.web.listener.AbstractStartupListener.contextInitialized(AbstractStartupListener.java:26) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4728) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5162) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1399) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745) Caused by: java.lang.NoSuchMethodError: org.quartz.SchedulerContext.put(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; at org.springframework.scheduling.quartz.SchedulerFactoryBean.populateSchedulerContext(SchedulerFactoryBean.java:634) at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:481) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1625) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1562) ... 24 more 看看哪位程序老大哥帮忙看看,谢谢! 问答

quartz项目启动到一半,不停止也不继续启动,求救

03-22

我一个quartz项目启动时没报错,但是不知道为什么就是不继续往下启动,控制台输出 2018-3-22 19:37:07 org.apache.catalina.core.AprLifecycleListener init 信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files (x86)\Java\jdk1.6.0_32\bin;E:\Tomcat 6.0\bin 2018-3-22 19:37:07 org.apache.coyote.http11.Http11Protocol init 信息: Initializing Coyote HTTP/1.1 on http-8080 2018-3-22 19:37:07 org.apache.catalina.startup.Catalina load 信息: Initialization processed in 579 ms 2018-3-22 19:37:07 org.apache.catalina.core.StandardService start 信息: Starting service Catalina 2018-3-22 19:37:07 org.apache.catalina.core.StandardEngine start 信息: Starting Servlet Engine: Apache Tomcat/6.0.33 2018-3-22 19:37:07 org.apache.catalina.startup.HostConfig deployDescriptor 信息: Deploying configuration descriptor manager.xml 2018-3-22 19:37:07 org.apache.catalina.startup.HostConfig deployDirectory 信息: Deploying web application directory bhylhlpt 2018-3-22 19:37:09 org.apache.catalina.core.ApplicationContext log 信息: Initializing Spring root WebApplicationContext 2018-3-22 19:37:19 org.quartz.simpl.SimpleThreadPool initialize 信息: Job execution threads will use class loader of thread: main 2018-3-22 19:37:19 org.quartz.core.SchedulerSignalerImpl <init> 信息: Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl 2018-3-22 19:37:19 org.quartz.core.QuartzScheduler <init> 信息: Quartz Scheduler v.1.8.5 created. 2018-3-22 19:37:20 org.quartz.impl.jdbcjobstore.JobStoreSupport initialize 信息: Using db table-based data access locking (synchronization). 2018-3-22 19:37:20 org.quartz.impl.jdbcjobstore.JobStoreCMT initialize 信息: JobStoreCMT initialized. 2018-3-22 19:37:20 org.quartz.core.QuartzScheduler initialize 信息: Scheduler meta-data: Quartz Scheduler (v1.8.5) 'schedule' with instanceId 'zz1521718639046' Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally. NOT STARTED. Currently in standby mode. Number of jobs executed: 0 Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads. Using job-store 'org.springframework.scheduling.quartz.LocalDataSourceJobStore' - which supports persistence. and is clustered. 2018-3-22 19:37:20 org.quartz.impl.StdSchedulerFactory instantiate 信息: Quartz scheduler 'schedule' initialized from an externally provided properties instance. 2018-3-22 19:37:20 org.quartz.impl.StdSchedulerFactory instantiate 信息: Quartz scheduler version: 1.8.5 2018-3-22 19:37:20 org.quartz.core.QuartzScheduler setJobFactory 信息: JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@166ff9c 2018-3-22 19:37:21 org.quartz.simpl.SimpleThreadPool initialize 信息: Job execution threads will use class loader of thread: main 2018-3-22 19:37:21 org.quartz.core.SchedulerSignalerImpl <init> 信息: Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl 2018-3-22 19:37:21 org.quartz.core.QuartzScheduler <init> 信息: Quartz Scheduler v.1.8.5 created. 2018-3-22 19:37:21 org.quartz.impl.jdbcjobstore.JobStoreSupport initialize 信息: Using db table-based data access locking (synchronization). 2018-3-22 19:37:21 org.quartz.impl.jdbcjobstore.JobStoreCMT initialize 信息: JobStoreCMT initialized. 2018-3-22 19:37:21 org.quartz.core.QuartzScheduler initialize 信息: Scheduler meta-data: Quartz Scheduler (v1.8.5) 'startCancleQuertz' with instanceId 'zz1521718641219' Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally. NOT STARTED. Currently in standby mode. Number of jobs executed: 0 Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads. Using job-store 'org.springframework.scheduling.quartz.LocalDataSourceJobStore' - which supports persistence. and is clustered. 2018-3-22 19:37:21 org.quartz.impl.StdSchedulerFactory instantiate 信息: Quartz scheduler 'startCancleQuertz' initialized from an externally provided properties instance. 2018-3-22 19:37:21 org.quartz.impl.StdSchedulerFactory instantiate 信息: Quartz scheduler version: 1.8.5 2018-3-22 19:37:21 org.quartz.core.QuartzScheduler setJobFactory 信息: JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@346239 我试着把quartz给注释掉就可以启动了,但是这个项目的主要功能都在quartz,注释了就等于废了,查了好久的百度都解决不了,求大神支招 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 点我我会动 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览