当前位置:首页 > 开发 > 系统架构 > 架构 > 正文

quartz源码解读

发表于: 2010-07-22   作者:Dead_knight   来源:转载   浏览次数:
摘要: quartz针对web应用提供两种方式配置【servlet、listener】,两种方式均在org.quartz.ee.servlet包中实现。 <servlet> <servlet-name>QuartzInitializer</servlet-name> <display-name>Quartz Initializer
quartz针对web应用提供两种方式配置【servlet、listener】,两种方式均在org.quartz.ee.servlet包中实现。
<servlet>  
	<servlet-name>QuartzInitializer</servlet-name>  
	<display-name>Quartz Initializer Servlet</display-name>  
	<servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class>
	<load-on-startup>1</load-on-startup>  
	<init-param>  
		<param-name>config-file</param-name>  
		<param-value>/conf/quartz.properties</param-value>  
	</init-param>  
	<init-param>  
		<param-name>shutdown-on-unload</param-name>  
		<param-value>true</param-value>  
	</init-param>  
	<init-param>  
		<param-name>start-scheduler-on-load</param-name>  
		<param-value>true</param-value>  
	</init-param>  
</servlet>

对于servlet方式,初始化servlet代码:
 StdSchedulerFactory factory;
        try {
            String configFile = cfg.getInitParameter("config-file");
            String shutdownPref = cfg.getInitParameter("shutdown-on-unload");
            if (shutdownPref != null) {
                performShutdown = Boolean.valueOf(shutdownPref).booleanValue();
            }
            // get Properties
            if (configFile != null) {
                factory = new StdSchedulerFactory(configFile)           
            } else {
                factory = new StdSchedulerFactory();
            }
            // Always want to get the scheduler, even if it isn't starting, 
            // to make sure it is both initialized and registered.
            scheduler = factory.getScheduler();            // Should the Scheduler being started now or later
            String startOnLoad = cfg
                    .getInitParameter("start-scheduler-on-load");
            int startDelay = 0;
            String startDelayS = cfg.getInitParameter("start-delay-seconds");
            try {
                if(startDelayS != null && startDelayS.trim().length() > 0)
                    startDelay = Integer.parseInt(startDelayS);
            } catch(Exception e) {
                log("Cannot parse value of 'start-delay-seconds' to an integer: " + startDelayS + ", defaulting to 5 seconds.", e);
                startDelay = 5;
            }
            /*
             * If the "start-scheduler-on-load" init-parameter is not specified,
             * the scheduler will be started. This is to maintain backwards
             * compatability.
             */
            if (startOnLoad == null || (Boolean.valueOf(startOnLoad).booleanValue())) {
                if(startDelay <= 0) {
                    // Start now
                    scheduler.start();
                    log("Scheduler has been started...");
                }
                else {
                    // Start delayed
                    scheduler.startDelayed(startDelay);
                    log("Scheduler will start in " + startDelay + " seconds.");
                }
            } else {
                log("Scheduler has not been started. Use scheduler.start()");
            }

            String factoryKey = cfg.getInitParameter("servlet-context-factory-key");
            if (factoryKey == null) {
                factoryKey = QUARTZ_FACTORY_KEY;
            }
            
            log("Storing the Quartz Scheduler Factory in the servlet context at key: "
                    + factoryKey);
            cfg.getServletContext().setAttribute(factoryKey, factory);

        } catch (Exception e) {
            log("Quartz Scheduler failed to initialize: " + e.toString());
            throw new ServletException(e);
        }

由于该servlet不提供get、post请求,所以重点在init方法中。
初始化主要完成两件事:一、通过调度器工厂产生调度器对象;二、启动调度器。
一、工厂如何产生调度器对象?
    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;
    }

首先在调度器仓库寻找是否存在未关闭的同名调度器(同名的意思为配置文件中的org.quartz.scheduler.instanceName属性值)【调度器仓库为单态模式】,存在则直接返回,否则实例化调度器,方法在instantiate中加以实现。

instantiate方法做的事情太多【rmi、jmx远程调度器代理、job存储、插件、监听器等等】,下面只对重要的片段进行简述
        ThreadPool tp = null;
        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;
        }
            ……………………
              tp.initialize();

线程池SimpleThreadPool初始化(根据org.quartz.threadPool属性组设置,线程池所维护的线程为WorkerThread,是SimpleThreadPool的内部类,主要执行实现JobRunShellFactory的工厂类产生的JobRunShell)
            JobRunShellFactory jrsf = null; 
            if (wrapJobInTx) {
                jrsf = new JTAJobRunShellFactory();
            } else {
                jrsf = new StdJobRunShellFactory();
            }

QuartzScheduler qs = null;
qs = new QuartzScheduler(rsrcs, schedCtxt, idleWaitTime, dbFailureRetry);

该对象的建立非常重要,因为在构造函数中新建QuartzSchedulerThread线程对象,此线程建立后,立刻启动。
this.schedThread = new QuartzSchedulerThread(this, resources, ctxt);

QuartzSchedulerThread主要扫描job存储机制,不过有个paused变量控制,即使调用start()方法,也实际上处于停止状态,需要等待QuartzScheduler的start方法触发执行命令:
schedThread.togglePause(false);
Scheduler scheduler = instantiate(rsrcs, qs);

    protected Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs) {
        SchedulingContext schedCtxt = new SchedulingContext();
        schedCtxt.setInstanceId(rsrcs.getInstanceId());

        Scheduler scheduler = new StdScheduler(qs, schedCtxt);
        return scheduler;
    }

至此,成功获得调度器实例。

二、启动调度器
Scheduler的所有任务都委托给QuartzScheduler执行,则启动调度器的操作在QuartzScheduler的实现为:
    public void start() throws SchedulerException {

        if (shuttingDown|| closed) {
            throw new SchedulerException(
                    "The Scheduler cannot be restarted after shutdown() has been called.");
        }

        if (initialStart == null) {
            initialStart = new Date();
            this.resources.getJobStore().schedulerStarted();            
            //初始化插件。即初始化quartz.properties中的org.quartz.plugin属性组
            startPlugins();
        }

        //此处代码为schedThread的线程开关 如果paused为true,则线程循环等待
        //如果设置为false,且存在job任务则执行
        schedThread.togglePause(false);

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

startPlugins()实际上是执行已配置插件的start方法。最常见的插件为:
//老版本的配置如v1.5
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin
org.quartz.plugin.jobInitializer.fileName = conf/quartz_jobs.xml
//新版本的配置如v1.8 可支持多个fileName,以,号分隔
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
org.quartz.plugin.jobInitializer.fileName = conf/quartz_jobs.xml

以上所配置插件的初始化,实际上就是组装jobDetail和Trigger到JobStrore中,让QuartzSchedulerThread线程轮询

午休了,待续【quartz集群、监听器、远程方式等】……

quartz源码解读

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
一、Quartz基本概念 Quartz 是 OpenSymphony开源组织在任务调度领域的一个开源项目,完全基于 Java
一、准备工作 1、下载Ant工具(版本要大于1.7),这里下载最新的版本1.8.4,下载地址: http://ant.a
网上对android WiFi源码解读的帖子也有不少,但大部分是android 2.3左右。最近研究了下android4.0
源码解读Mybatis List列表In查询实现的注意事项 在SQL开发过程中,动态构建In集合条件查询是比较常
今天的这一节,将从整体上对mina的源代码进行把握,网上已经有好多关于mina源码的阅读笔记,但好多
今天的这一节,将从整体上对mina的源代码进行把握,网上已经有好多关于mina源码的阅读笔记,但好多
上一篇:地址 先解决上次留下的疑问,开始看到zepto.z[0]这个东西的时候,我很是不爽,看着它都不顺
1、groupcache介绍: http://www.csdn.net/article/2013-07-30/2816399-groupcache-readme-go 2、gr
HTTP/1.1 默认的连接方式是长连接,不能通过简单的TCP连接关闭判断HttpMessage的结束。 以下是几种
网上对android WiFi源码解读的帖子也有不少,但大部分是android 2.3左右。最近研究了下android4.0
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号