zoukankan      html  css  js  c++  java
  • spring-定时器(2)

    Spring提供的三种定时任务机制及其比较

    定时任务的需求在众多应用系统中广泛存在,在Spring中,我们可以使用三种不同的定时机制,下面一一描述并加以比较

    1. 基于Quartz的定时机制

     Spring基于Quartz的定时机制

    下面详细解释这个类图中涉及的关键类及其使用场景

    1.1. SchedulerFactoryBean

    这是Spring中基于Quartz的定时机制入口,只要Spring容器装载了这个类,Quartz定时机制就会启动,并加载定义在这个类中的所有trigger

    Spring配置范例:

        <bean id="sfb" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
         <!-- 添加触发器 -->  
         <property name="triggers">  
          <list>  
           <ref local="appSubscTrigger" />  
          </list>  
         </property>  
                  
         <!-- 添加listener -->  
         <property name="globalTriggerListeners">  
          <list>  
           <ref local="myTaskTriggerListener" />  
          </list>  
         </property>  
        </bean>  

    1.2. CronTriggerBean

    实现了Trigger接口,基于Cron表达式的触发器

    这种触发器的好处是表达式与linux下的crontab一致,能够满足非常复杂的定时需求,也容易配置

    Spring配置范例:

    <bean id="notifyTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">  
     <property name="jobDetail" ref="notifyJobDetail" />  
     <property name="cronExpression" value="${notify_trigger_cron_expression}" />  
    </bean> 

    1.3. SimpleTriggerBean

    该类也实现了Trigger接口,基于配置的定时调度

    这个触发器的优点在于很容易配置一个简单的定时调度策略

    Spring配置范例

        <bean id="simpleReportTrigger"   class="org.springframework.scheduling.quartz.SimpleTriggerBean">   
         <property name="jobDetail">   
          <ref bean="reportJob"/>   
         </property>   
         <property name="startDelay">   
          <value>3600000</value>   
         </property>   
         <property name="repeatInterval">   
          <value>86400000</value>   
         </property>   
        </bean>  

    1.4. JobDetailBean

    JobDetail类的简单扩展,能够包装一个继承自QuartzJobBean的普通Bean,使之成为定时运行的Job

    缺点是包装的Bean必须继承自一个指定的类,通用性不强,对普通Job的侵入性过强,不推荐使用

    1.5. MethodInvokingJobDetailFactoryBean

    Spring提供的一个不错的JobDetail包装工具,能够包装任何bean,并执行类中指定的任何stati或非static的方法,避免强制要求bean去实现某接口或继承某基础类

        <bean id="notifyJobDetail" parent="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
         <property name="targetObject" ref="notifyServerHandler" />  
         <property name="targetMethod" value="execute" />  
        </bean>  

    1.6. 关于TriggerListener和JobListener

    Quartz中提供了类似WebWork的拦截器的功能,系统执行任务前或任务执行完毕后,都会检查是否有对应的Listener需要被执行,这种AOP的思想为我们带来了灵活的业务需求实现方式。

    例如现在有一个简单的业务要求:任务执行前先判断当前服务器是否为task服务器,不是则不执行任务。对于这种业务需求,我们可以简单的实现一个 TriggerListener,并将其插入SchedulerFactoryBean的globalTriggerListeners中,这样所有的 job在执行前后都会调用TriggerListener中对应的方法。

    代码范例:

    public class MyTaskTriggerListener implements TriggerListener {
        protected static final Log logger = LogFactory.getLog(MyTaskTriggerListener.class);
    
        /**
         * 需要运行task任务的机器列表
         */
        private String taskServers;
    
        public String getName() {
            return "MyTaskTriggerListener";
        }
    
        public void triggerComplete(Trigger arg0, JobExecutionContext arg1, int arg2) {
        }
    
        public void triggerFired(Trigger arg0, JobExecutionContext arg1) {
        }
    
        public void triggerMisfired(Trigger arg0) {
        }
    
        /**
         * 判断当前服务器是否为task服务器,来决定是否执行task
         * @return
         */
        public boolean vetoJobExecution(Trigger arg0, JobExecutionContext arg1) {
            String serverName;
            try {
                serverName = InetAddress.getLocalHost().getHostName();//获取主机名
            } catch (UnknownHostException e) {
                e.printStackTrace();
                return true;
            }
            if (taskServers.indexOf(serverName) > -1) {
                if (logger.isInfoEnabled()) {
                    logger.info("this is a task server, job will be executed");
                }
                return false;
            } else {
                if (logger.isInfoEnabled()) {
                    logger.info("this is not a task server, job will be vetoed");
                }
                return true;
            }
        }
    
        public String getTaskServers() {
            return taskServers;
        }
    
        public void setTaskServers(String taskServers) {
            this.taskServers = taskServers;
        }
    }

    2. 基于Timer的定时机制

    JDK提供了基础的定时类:Timer,在这个类的基础上,Spring提供了一套简单的定时机制

    Spring基于Timer的定时机制

    下面详细解释这个类图中涉及的关键类及其使用场景

    2.1. TimerFactoryBean

    这个类非常类似Quartz中的SchedulerFactoryBean,是基于Timer的定时机制的入口,Spring容器装载此类后会自动开始定时器

    Spring配置范例:

    <bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
     <property name="scheduledTimerTasks">
      <list>
       <ref bean="scheduledTask" />
      </list>
     </property>
    </bean>

    2.2. ScheduledTimerTask

    类似于Quartz中的Trigger的SimpleTriggerBean实现,任务是在设定的时间触发并执行配置的任务,特点是配置简单、明了,使用于简单的任务触发逻辑

    Spring配置范例:

    <bean id=”scheduledReportTask” class=”org.springframework.scheduling.timer.ScheduledTimerTask”>
     <property name=”timerTask”>
      <ref bean =”reportTimerTask”/>
     </property>
     <property name=”period”>
      <value>86400000</value>
     </property>
    </bean>

    2.3. TimerTask抽象类

    普通task实现必须要继承的父类,主要包含一个run()的方法,类似Quartz中的QuartzJobBean,对应用侵入性较强,也不推荐使用

    2.4. MethodInvokingTimerTaskFactoryBean

    类似Quartz中的MethodInvokingJobDetailFactoryBean,用于封装任何bean,并可以执行bean中的任意方法,不再复述

    3. 基于Executor的定时机制

    Spring基于Executor的定时机制 

    这种定时机制与上面两种定时机制没有太大区别,特别是在配置和实现功能上,不同的是它的核心是基于ScheduledExecutorService(ScheduledThreadPoolExecutor是默认实现),一种JDK5.0中提供的基于线程的并发机制,关于JDK5中的线程池的概念及其一些深入分析,请参考老唐的博客:http://blog.csdn.net/sfdev/archive/2008/12/30/3648457.aspx 这里不再复述

    4. 三种定时机制的比较和案例分析

    看完了这三种定时机制,各有各的优劣,不同场景下我们应该灵活选择不同的定时机制。总的来说,如果我们需要简单的定时器,我们可以选用基于 timer的定时器,如果定时规则较为复杂,我们可以选用基于Quartz的定时器,如果我们要用到线程池来处理异步任务,我们可以选用基于 Executor的定时机制,虽然只是任务实现中用到线程池,毕竟也是一脉相承的,当然也可以用Quartz的定时器+基于Executor的任务线程 池,完全没有任何冲突的。

    说这么多,还是比较抽象,不如我们来分析一下老唐的Notify系统来加深对Spring定时机制的了解(详细设计参考最近一期的程序员杂志)。

    在老唐的Notify系统中,完全使用了基于JDK5.0中的Executor的定时机制,即由一个 ScheduledExecutorFactoryBean触发系统的每隔2分钟运行一个单线程的任务,在这个任务中,执行完各种机制检查和配置策略后, 将要执行的Notify任务放入一个已配置好的线程池,并由线程池指定线程来完成Notify的任务。

    在最近一期的项目中,我们将task移植到了apps,Notify系统也同时被移植过来了,为了统一所有的task,我们将以前task中基于 timer、Quartz和Executor的各种任务统一改为基于Quartz的调度。在这个过程中,Notify系统的基于Executor的定时机 制也被改为基于Quartz的定时机制,过程非常顺利。基于这次移植项目,可以说这三种定时机制是非常容易互换的,并且通用性比较强,只需要简单的配置即 可。

    转自:http://blog.csdn.net/52rainbow/article/details/4004206

  • 相关阅读:
    百度面试题
    分治法--二分查找、乘方、斐波那契数
    01-11李宁老师学Python视频课程(1):初识Python返回课程
    邮件发送的两种实现方法。
    Docker(一):Docker入门教程
    安装docker及在docker中安装python环境学
    vim编辑器的使用和CentOS有很多不同
    大一编程基础培训]==02-03-04-05课==类型
    大一编程基础培训]==08课==条件判断]==07课==Python的LIST与TUPLE数据类型
    Beautiful Soup 4.2.0 文档¶ BeautifulSoup对象内任何第一个标签入口,使用find()方法。
  • 原文地址:https://www.cnblogs.com/crazylqy/p/4227913.html
Copyright © 2011-2022 走看看