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

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/beliefer/article/details/52278984 前言在《Quartz与Spring集成——创建调度器》一文中介绍了调度器的创建过程,本文将分析其启动过程。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/beliefer/article/details/52278984

前言

《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 


相关文章
|
2月前
|
安全 Java 数据库
后端进阶之路——万字总结Spring Security与数据库集成实践(五)
后端进阶之路——万字总结Spring Security与数据库集成实践(五)
|
3月前
|
NoSQL Java Redis
Spring Boot集成Redisson详细介绍
Redisson是一个用于Java的分布式和高可用的Java对象的框架,它基于Redis实现。在Spring Boot应用程序中集成Redisson可以帮助我们更轻松地实现分布式锁、分布式对象、分布式集合等功能。本文将介绍如何在Spring Boot项目中集成Redisson,并展示一些基本用法。
483 2
Spring Boot集成Redisson详细介绍
|
18天前
|
SQL Java 调度
SpringBoot集成quartz定时任务trigger_state状态ERROR解决办法
SpringBoot集成quartz定时任务trigger_state状态ERROR解决办法
|
1月前
|
Java 数据库连接 数据库
Spring Boot整合MyBatis Plus集成多数据源轻松实现数据读写分离
Spring Boot整合MyBatis Plus集成多数据源轻松实现数据读写分离
26 2
|
1月前
|
监控 NoSQL Java
Spring Boot集成Redis启动失败【Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.G】
Spring Boot集成Redis启动失败【Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.G】
|
1月前
|
存储 NoSQL Java
如何使用Spring Boot与Redis集成
2月更文挑战第12天】
54 0
|
2月前
|
SQL Java 关系型数据库
【极光系列】springBoot集成xxl-job调度器
【极光系列】springBoot集成xxl-job调度器
|
10月前
|
Java 调度 Maven
spring boot 2.0 quartz 轻松实现定时任务和作业调度
spring boot 2.0 quartz 轻松实现定时任务和作业调度
97 0
|
存储 消息中间件 SQL
spring-boot-route(二十一)quartz实现动态定时任务
spring-boot-route(二十一)quartz实现动态定时任务
208 0
spring-boot-route(二十一)quartz实现动态定时任务
|
XML Java 调度
JAVA | Spring + quartz 实现定时任务
很久不见,因为忙着泡妞,断更了一个月,实在是罪过。废话不多说,最近在工作中遇到了使用 quartz 实现定时任务的需求。写出来分享给大家,权当笔记。
JAVA | Spring + quartz 实现定时任务