企業任務調度:quartz 基礎知識(一)

1. quartz

 

    Quartz是一個作業調度系統(a job scheduling system),負責在約定的時間到達時執行(或通知)其他軟件控件。Quartz是以.jar文件的形式發佈的,Quartz java庫中包含了Quartz所有的核心功能,該功能的主要接口(API)是Scheduler接口,Scheduler接口提供了一些簡單的操作,如:schedulering/unscheduling jobs,starting/stopping/pausing the scheduler。

 

    如果我們要調度自定義的作業,該作業必須實現Job接口,Job接口包含了一個必須實現的方法:execute(…);如果我們需要實現當到達被調度的時間能夠得到通知,我們必須實現TriggerListener或JobListener接口。

 

    Quartz主要程序不但能夠作爲獨立的應用(帶有RMI 接口)運行,也可以作爲J2EE組件資源在J2EE應用服務器運行。

 

 

2. Schedule和ScheduleFactory

 

    使用Scheduler前必須實例化Scheduler,需要由SchedulerFactory類來創建Scheduler,Factory的實例可以通過在JNDI存儲中的Factory的序列化的方式來獲取,實例化Factory後直接使用該實例也是很容易的,如下面的例子。

 

    Scheduler被實例化後就可以啓動、暫停或關閉,但是Scheduler一旦關閉就不能再次啓動該Scheduler,只有必須再次實例化後纔可以;Trigger只有與之對應的Scheduler啓動後才能觸發與之相關的Job,否則一直處於暫停狀態。

 

    下面是一個實例化、啓動Scheduler和調度執行作業的例子:

//創建一個SchedulerFactory類的實例
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
//創建一個Scheduler類的實例
Scheduler sched = schedFact.getScheduler();
//啓動上面創建的Scheduler
sched.start();
//創建一個JobDetail
JobDetail jobDetail = new JobDetail("myJob",
                                          sched.DEFAULT_GROUP,
                                        DumbJob.class);
//創建一個SimpleTrigger
SimpleTrigger trigger = new SimpleTrigger("myTrigger",
                                              sched.DEFAULT_GROUP,
                                                new Date(),
                                              null,
                                              0,
                                              0L);
//把JobDetail和SimpleTrigger所創建的各自實例關聯到一個Scheduler中
sched.scheduleJob(jobDetail, trigger);

 

 

3. Trigger 觸發器

 

    Trigger對象是用於觸發Job的執行,爲了調度一個作業,我們需要實例化一個Trigger,並根據作業的需求設置該Trigger的屬性,Trigger有兩種:SimpleTrigger和CronTrigger。

 

    SimpleTrigger只適用於在規定的時刻觸發Job的執行,或在規定的期限內按一定的時間間隔重複觸發Job的執行;當我們需要基於日期類型(如:每星期5中午或每個月的第10天10:15)的作業調度時,需要用到CronTrigger。

 

    在Quartz中Job與Trigger之間是相互獨立的,可以對Job和Trigger分別進行各自的定義設置,然後根據需要進行組合,這樣就可以實現一個Job可以與許多不同的Trigger相關聯,一個Trigger也可以關聯到不同的Job上,達到了鬆散耦合的目的,當需要更改一個Job的Trigger時,只需要修改或替換對應的Trigger就可以了,而無須重新定義一個與該Job關聯的Scheduler。

 

 

4. Job

 

上面已經提及到了,我們可以使Scheduler簡單地執行一個實現了Job接口的Java組件,Job接口定義如下:

 

package org.quartz;
public interface Job {
     public void execute(JobExecutionContext context)
         throws JobExecutionException;
}

 

    當Job的Trigger執行觸發後,Scheduler將調用執行execute(…)方法,JobExecutionContext對象是該方法的參數,爲Job實例提供了一個執行該Job的Scheduler的handle、一個觸發該execute方法的Trigger的handle、Job的JobDetail對象和一些其他運行時的環境變量值。

 

    JobDetail對象是在Quartz的客戶端(我們的程序)在Job被關聯加入到Scheduler時創建的,JobDetail包含了Job的各種屬性值和JobDataMap,JobDataMap中存放了與之相關的Job類實例的狀態信息。

 

作業和作業詳情(Jobs&JobDetails)


    Jobs的實現相對來說比較容易,只需要理解Job的性質、Job接口中的execute(…)方法和JobDetails。

我們真正需要實現的類實際上是Job的內容,通過JobDetail類向Quartz傳遞有關該Job的各種屬性值。

 

接下來介紹有關Job的性質和Job在Quartz中的生命週期,以下是前面提到的代碼:

 

JobDetail jobDetail = new JobDetail("myJob",               // job name
                                        sched.DEFAULT_GROUP, // job group
                                          DumbJob.class);          // the java class to execute
SimpleTrigger trigger = new SimpleTrigger("myTrigger",
                                              sched.DEFAULT_GROUP,
                                              new Date(),
                                                null,
                                              0,
                                              0L);
sched.scheduleJob(jobDetail, trigger);

 

    可以看到我們向Scheduler的實例sched中加入了JobDetail類的實例jobDetail,同時在jobDetail中注入了我們需要執行的類DumbJob.class。sched每次在調用執行execute(…)方法前都會對所調用的類DumbJob.class創建一個新的實例,這樣就使得Job必須有一個沒有參數的構造函數,並且在Job類中定義的成員變量每次都會被初始化。

 

    哪我們如何爲Job類提供屬性或配置信息呢?如何跟蹤兩個執行任務之間的狀態呢?Quartz提供了JobDataMap來實現,JobDataMap是JobDetail 對象的一部分。

 

    在Job實例運行期間對應的JobDataMap中能夠容納任意多的對象,JobDataMap是對Java Map接口的實現,並增加一些對於原始類型存儲和檢索的方法。

 

    下列是向JobDataMap對象存儲數據的一些代碼:

 

jobDetail.getJobDataMap().put("jobSays", "Hello World!");
jobDetail.getJobDataMap().put("myFloatValue", 3.141f);
jobDetail.getJobDataMap().put("myStateData", new ArrayList());

 

 

    下列是在Job執行過程中獲取JobDataMap有關數據的例子:

 

public class DumbJob implements Job {
      public DumbJob() {
      }
      public void execute(JobExecutionContext context) throws JobExecutionException
      {
        String instName = context.getJobDetail().getName();//獲取Job實例的name
        String instGroup = context.getJobDetail().getGroup();//獲取Job實例的group
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();//獲取JobDataMap對象
        String jobSays = dataMap.getString("jobSays");//獲取JobDataMap中對象jobSays的值
        float myFloatValue = dataMap.getFloat("myFloatValue");//獲取JobDataMap中對象myFloatValue的值
        ArrayList state = (ArrayList)dataMap.get("myStateData");
        state.add(new Date());//獲取JobDataMap中對象myStateData的值
        System.err.println("Instance " + instName + " of DumbJob says: " + jobSays);
      }
}

 

有狀態vs.無狀態的作業(Stateful vs. Non-Stateful Jobs)


    這部分介紹Job的狀態,即有關JobDataMap內容,Job實例可以分爲:有狀態和無狀態;無狀態Job只在註冊進Scheduler時纔有JobDataMap數據,無狀態Job運行結束後不保存JobDataMap數據,這樣意味着在無狀態Job運行過程中修改過的JobDataMap數據在無狀態Job再次運行時將無法得到;而有狀態Job與之相反,有狀態Job每次運行結束時將存儲有狀態Job的JobDataMap數據,但是有狀態Job爲了保證JobDataMap的數據的一致性,有狀態Job無法並行運行,當Trigger需要再次觸發執行一個正在運行的Job時,Trigger會自動的延遲觸發,直到正在運行的Job運行結束後纔再次觸發該Job。

 

    我們需要有狀態Job時必須實現StatefulJob接口,而不是上面的例子中的Job接口。

 

    作業的其他屬性(Other Attributes Of Jobs)


    以下對Job實例的其他屬性進行介紹:

 

l         持久性(Durability):如果Job是非持久性的,一旦沒有活動的Trigger與之相關聯時,該Job會自動從Scheduler中刪除掉;

 

l         易揮發性(Volatility):如果Job是易揮發的,當與之相關聯的Schuduler停止之後,不會通過JobStore對該Job進行保存;

 

l         可恢復性(RequestsRecovery):如果Job是可恢復的,該Job運行期間與之關聯的Scheduler非正常停止(由於進程停止或機器關閉等引起)時,當該Scheduler再次啓動時,該Job會重新執行一次;

 

l         JobListener:一個Job可以被關聯到一個或多個JobListener,當該Job執行時,與之關聯的JobListener會得到通知;

 

   作業接口中的execute(…)方法(The Job.execute(…) Method)


    Execute方法只允許有一種類型的異常(包括RuntimeException)可以拋出,該異常就是JobExecutionException;因此我們必須把execute方法的所有內容放在try-catch語句中。當需要了解如何處理異常時,我們還必須閱讀有關JobExecutionException的JavaDoc文檔。

 

5. name和group

 

    註冊進Quartz Scheduler中的Job和Trigger是通過name來標識的,爲了後期的維護,Job和Trigger能夠按類劃分爲group,在一個group中每個Job和Trigger的name必須爲唯一的,即Job和Trigger的標識是由各自的name+group組成的。

 

 

 

 

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