`
newleague
  • 浏览: 1501029 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类

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

阅读更多

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

1. 基于Quartz的定时机制

Spring基于Quartz的定时机制
 

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

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

Spring配置范例:

view plaincopy to clipboardprint?
<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> 
<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配置范例:

view plaincopy to clipboardprint?
<bean id="notifyTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> 
 <property name="jobDetail" ref="notifyJobDetail" /> 
 <property name="cronExpression" value="${notify_trigger_cron_expression}" /> 
</bean> 
<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配置范例:

view plaincopy to clipboardprint?
<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> 
<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去实现某接口或继承某基础类

Spring配置范例:

view plaincopy to clipboardprint?
<bean id="notifyJobDetail" parent="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> 
 <property name="targetObject" ref="notifyServerHandler" /> 
 <property name="targetMethod" value="execute" /> 
</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中对应的方法。

代码范例:

view plaincopy to clipboardprint?
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;  
    }  

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配置范例:

view plaincopy to clipboardprint?
<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean"> 
 <property name="scheduledTimerTasks"> 
  <list> 
   <ref bean="scheduledTask" /> 
  </list> 
 </property> 
</bean> 
<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配置范例:

+ expand sourceview plaincopy to clipboardprint?
<bean id=”scheduledReportTask” class=”org.springframework.scheduling.timer.ScheduledTimerTask”> 
 <property name=”timerTask”> 
  <ref bean =”reportTimerTask”/> 
 </property> 
 <property name=”period”> 
  <value>86400000</value> 
 </property> 
</bean> 
<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的定时机制,过程非常顺利。基于这次移植项目,可以说这三种定时机制是非常容易互换的,并且通用性比较强,只需要简单的配置即可。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/52rainbow/archive/2009/03/19/4004206.aspx

http://book.51cto.com/art/200909/149346.htm

分享到:
评论

相关推荐

    基于Springboot的实验报告系统源码数据库文档.zip

    基于Springboot的实验报告系统源码数据库文档.zip

    ERA5_Climate_Single_Month.txt

    GEE训练教程——Landsat5、8和Sentinel-2、DEM和各2哦想指数下载

    基于springboot智能健康饮食系统源码数据库文档.zip

    基于springboot智能健康饮食系统源码数据库文档.zip

    基于SpringBoot的校园服务系统源码数据库文档.zip

    基于SpringBoot的校园服务系统源码数据库文档.zip

    史上最全IXIA测试仪配置使用指导手册(含IxNetwork,图文并茂超详细!).zip

    内容概要: IXIA测试仪的基本配置.doc ixia测试仪基础使用示例.doc IxNetwork如何进行抓包回放-V1.0.pdf IxNetwork如何自定义报文-V2.0.pdf ixia构造ip分片方法.txt IxNetwork使用简介.pdf 适用人群:网络协议造包、打流相关的测试工程技术人员,想要学习的同学可以下载哈 使用场景:构造pcap包,打流 Ixia简介 IXIA使用的是Server-client模式,Server端在测试仪表的主机上,在开机后会随着主机内的操作系统的启动而自动启动,一般情况下不需要人为的手工启动。因此在通常不需要为主机配置专用的显示器和键盘。 client端包括两个测试软件: Ixia Explorer和ScriptMate。这两个软件一般安装在测试用计算机上,在仪表自带的主机中也有这两个软件。根据测试项目的不同来选择使用不同的软件。Ixia Explorer主要提供数据流的测试,针对设备的功能进行测试; ScriptMate提供各种性能测试窗口,针对设备的性能进行测试。 Auto:自动分配;

    基于Python+Django花卉商城系统源码数据库文档.zip

    基于Python+Django花卉商城系统源码数据库文档.zip

    Umi-OCR-main.zip

    Umi-OCR-main.zip

    微信小程序源码-促销抽奖.zip

    基于微信小程序开发的促销抽奖小工具源码,适用于初学者了解小程序开发过程以及简单抽奖工具的实现。

    Sen2_median.txt

    GEE训练教程——Landsat5、8和Sentinel-2、DEM和各2哦想指数下载

    springboot的概要介绍与分析

    以下是一个关于Spring Boot设计的资源描述及项目源码的简要概述: Spring Boot设计资源描述 Spring Boot是一个为基于Spring的应用提供快速开发工具的框架,其设计旨在简化Spring应用的初始搭建和开发过程。以下是一些关键资源: Spring Boot官方文档:详细阐述了Spring Boot的核心特性、自动配置原理、起步依赖、内嵌式服务器等关键概念。这是学习和掌握Spring Boot设计的首选资源。 在线教程与视频:各大在线教育平台提供了丰富的Spring Boot教程和视频课程,从基础入门到高级应用,帮助开发者全面了解和掌握Spring Boot设计。 书籍与电子资料:许多技术书籍和在线电子资料深入讲解了Spring Boot的设计原理、最佳实践和项目案例,为开发者提供了宝贵的学习资源。 项目源码示例 以下是一个简单的Spring Boot项目源码示例,用于演示Spring Boot的基本结构和自动配置功能: java // 引入Spring Boot依赖 @SpringBootApplication public class MySpri

    基于springboot美妆领域管理系统源码数据库文档.zip

    基于springboot美妆领域管理系统源码数据库文档.zip

    tables-3.7.0+gpl-cp37-cp37m-win_amd64.whl

    tables-3.7.0+gpl-cp37-cp37m-win_amd64.whl

    算法实现的概要介绍与分析

    算法是计算机科学的核心,它们在解决各种问题时发挥着关键作用。一个好的算法不仅可以提高程序的效率,还可以简化复杂的问题。下面我将通过一个具体的例子——快速排序算法(Quick Sort)——来展示算法的实现过程,包括资源描述和项目源码。 ### 快速排序算法简介 快速排序是一种高效的排序算法,采用分治法的思想。其基本步骤如下: 1. 从数列中挑出一个元素,称为“基准”(pivot)。 2. 重新排序数列,所有比基准值小的元素放到基准前面,所有比基准值大的元素放到基准后面(相同的数可以到任一边)。在这个分割结束之后,该基准就处于数列的中间位置。这个称为分割(partition)操作。 3. 递归地(recursive)把小于基准值的子数列和大于基准值的子数列排序。 ### 资源描述 快速排序算法因其高效性和简洁性,在实际应用中非常受欢迎。它的时间复杂度平均为 O(n log n),最坏情况下为 O(n^2),但这种情况很少发生。快速排序的空间复杂度为 O(log n),因为它使用了递归来实现。 快速排序的一个典型应用场景是在数据库系统中对大量数据进行排序。由于它的高效性,快速排序

    基于springboot农场投入品运营线上管理系统源码数据库文档.zip

    基于springboot农场投入品运营线上管理系统源码数据库文档.zip

    基于springboot个性化影院推荐系统源码数据库文档.zip

    基于springboot个性化影院推荐系统源码数据库文档.zip

    linux基础进阶笔记

    linux基础进阶笔记,配套视频:https://www.bilibili.com/list/474327672?sid=4493093&spm_id_from=333.999.0.0&desc=1

    微信自动抢红包动态库.zip程序资源学习资料参考

    小程序 微信自动抢红包动态库.zip程序资源学习资料参考

    iOS版微信抢红包插件(支持后台抢红包).zip

    小程序 iOS版微信抢红包插件(支持后台抢红包).zip

    经典-FPGA时序约束教程

    经典-FPGA时序约束教程

    基于springboot的智慧点餐系统源码数据库文档.zip

    基于springboot的智慧点餐系统源码数据库文档.zip

Global site tag (gtag.js) - Google Analytics