Spring提供的三種定時任務機制及其比較

 定時任務的需求在衆多應用系統中廣泛存在,在Spring中,我們可以使用三種不同的定時機制,下面一一描述並加以比較

1. 基於Quartz的定時機制

 Spring基於Quartz的定時機制

下面詳細解釋這個類圖中涉及的關鍵類及其使用場景

1.1. SchedulerFactoryBean

這是Spring中基於Quartz的定時機制入口,只要Spring容器裝載了這個類,Quartz定時機制就會啓動,並加載定義在這個類中的所有trigger

 

Spring配置範例:

 

 

1.2. CronTriggerBean

實現了Trigger接口,基於Cron表達式的觸發器

 

這種觸發器的好處是表達式與linux下的crontab一致,能夠滿足非常複雜的定時需求,也容易配置

 

Spring配置範例:

 

 

1.3. SimpleTriggerBean

該類也實現了Trigger接口,基於配置的定時調度

 

這個觸發器的優點在於很容易配置一個簡單的定時調度策略

 

Spring配置範例:

 

 

 

1.4. JobDetailBean

JobDetail類的簡單擴展,能夠包裝一個繼承自QuartzJobBean的普通Bean,使之成爲定時運行的Job

 

缺點是包裝的Bean必須繼承自一個指定的類,通用性不強,對普通Job的侵入性過強,不推薦使用

1.5. MethodInvokingJobDetailFactoryBean

Spring提供的一個不錯的JobDetail包裝工具,能夠包裝任何bean,並執行類中指定的任何stati或非static的方法,避免強制要求bean去實現某接口或繼承某基礎類

 

Spring配置範例:

 

 

 

 

1.6. 關於TriggerListener和JobListener

Quartz中提供了類似WebWork的攔截器的功能,系統執行任務前或任務執行完畢後,都會檢查是否有對應的Listener需要被執行,這種AOP的思想爲我們帶來了靈活的業務需求實現方式。

 

例如現在有一個簡單的業務要求:任務執行前先判斷當前服務器是否爲task服務器,不是則不執行任務。對於這種業務需求,我們可以簡單的實現一個TriggerListener,並將其插入SchedulerFactoryBean的globalTriggerListeners中,這樣所有的job在執行前後都會調用TriggerListener中對應的方法。

 

代碼範例:

 

  1. public class MyTaskTriggerListener implements TriggerListener {  
  2.     protected static final Log logger = LogFactory.getLog(MyTaskTriggerListener.class);  
  3.   
  4.     /** 
  5.      * 需要運行task任務的機器列表 
  6.      */  
  7.     private String taskServers;  
  8.   
  9.     public String getName() {  
  10.         return "MyTaskTriggerListener";  
  11.     }  
  12.   
  13.     public void triggerComplete(Trigger arg0, JobExecutionContext arg1, int arg2) {  
  14.     }  
  15.   
  16.     public void triggerFired(Trigger arg0, JobExecutionContext arg1) {  
  17.     }  
  18.   
  19.     public void triggerMisfired(Trigger arg0) {  
  20.     }  
  21.   
  22.     /** 
  23.      * 判斷當前服務器是否爲task服務器,來決定是否執行task 
  24.      * @return 
  25.      */  
  26.     public boolean vetoJobExecution(Trigger arg0, JobExecutionContext arg1) {  
  27.         String serverName;  
  28.         try {  
  29.             serverName = InetAddress.getLocalHost().getHostName();//獲取主機名  
  30.         } catch (UnknownHostException e) {  
  31.             e.printStackTrace();  
  32.             return true;  
  33.         }  
  34.         if (taskServers.indexOf(serverName) > -1) {  
  35.             if (logger.isInfoEnabled()) {  
  36.                 logger.info("this is a task server, job will be executed");  
  37.             }  
  38.             return false;  
  39.         } else {  
  40.             if (logger.isInfoEnabled()) {  
  41.                 logger.info("this is not a task server, job will be vetoed");  
  42.             }  
  43.             return true;  
  44.         }  
  45.     }  
  46.   
  47.     public String getTaskServers() {  
  48.         return taskServers;  
  49.     }  
  50.   
  51.     public void setTaskServers(String taskServers) {  
  52.         this.taskServers = taskServers;  
  53.     }  
  54. }  

 

 

2. 基於Timer的定時機制

JDK提供了基礎的定時類:Timer,在這個類的基礎上,Spring提供了一套簡單的定時機制

Spring基於Timer的定時機制

下面詳細解釋這個類圖中涉及的關鍵類及其使用場景

 

2.1. TimerFactoryBean

這個類非常類似Quartz中的SchedulerFactoryBean,是基於Timer的定時機制的入口,Spring容器裝載此類後會自動開始定時器

 

Spring配置範例:

 

 

 

2.2. ScheduledTimerTask

類似於Quartz中的Trigger的SimpleTriggerBean實現,任務是在設定的時間觸發並執行配置的任務,特點是配置簡單、明瞭,使用於簡單的任務觸發邏輯

 

Spring配置範例:

 

 

 

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的定時機制,過程非常順利。基於這次移植項目,可以說這三種定時機制是非常容易互換的,並且通用性比較強,只需要簡單的配置即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章