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 Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
24天前
|
XML Java API
Spring Boot集成MinIO
本文介绍了如何在Spring Boot项目中集成MinIO,一个高性能的分布式对象存储服务。主要步骤包括:引入MinIO依赖、配置MinIO属性、创建MinIO配置类和服务类、使用服务类实现文件上传和下载功能,以及运行应用进行测试。通过这些步骤,可以轻松地在项目中使用MinIO的对象存储功能。
|
26天前
|
消息中间件 Java Kafka
什么是Apache Kafka?如何将其与Spring Boot集成?
什么是Apache Kafka?如何将其与Spring Boot集成?
63 5
|
28天前
|
消息中间件 Java Kafka
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
43 1
|
1月前
|
存储 Java 调度
Sppring集成Quartz简单案例详解 包括(添加、停止、恢复、删除任务、获取下次执行时间等)
Sppring集成Quartz简单案例详解 包括(添加、停止、恢复、删除任务、获取下次执行时间等)
33 2
|
29天前
|
消息中间件 监控 Java
您是否已集成 Spring Boot 与 ActiveMQ?
您是否已集成 Spring Boot 与 ActiveMQ?
53 0
|
2月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
234 1
|
2月前
|
Java Spring
springboot 学习十一:Spring Boot 优雅的集成 Lombok
这篇文章是关于如何在Spring Boot项目中集成Lombok,以简化JavaBean的编写,避免冗余代码,并提供了相关的配置步骤和常用注解的介绍。
124 0
|
5月前
|
监控 druid Java
spring boot 集成配置阿里 Druid监控配置
spring boot 集成配置阿里 Druid监控配置
322 6
|
5月前
|
Java 关系型数据库 MySQL
如何实现Springboot+camunda+mysql的集成
【7月更文挑战第2天】集成Spring Boot、Camunda和MySQL的简要步骤: 1. 初始化Spring Boot项目,添加Camunda和MySQL驱动依赖。 2. 配置`application.properties`,包括数据库URL、用户名和密码。 3. 设置Camunda引擎属性,指定数据源。 4. 引入流程定义文件(如`.bpmn`)。 5. 创建服务处理流程操作,创建控制器接收请求。 6. Camunda自动在数据库创建表结构。 7. 启动应用,测试流程启动,如通过服务和控制器开始流程实例。 示例代码包括服务类启动流程实例及控制器接口。实际集成需按业务需求调整。
428 4