Quartz,企業級的計劃/排程(job schedule)系統(1)-介紹

來源:互聯網
上載者:User

什麼是job schedule system?    job schedule system是負責在預定義的時間執行或者通知某個應用組件的系統。舉個例子來說,比如在每周一早上9:30發送email通知客戶最新的業務情況。

java.util.Timer和java.util.TimerTask    Timer和TimerTask是可以完成job schedule的兩個jdk提供的類,不過這不能稱為一個system。Timer和TimerTask是很簡單的,不直接支援持久化任務,線程池和類似日曆(calendar-like)的計劃安排,在完成一些進階功能上開發人員要進行大量的擴充。

Quartz的簡單介紹    Quartz是opensymphony組織專攻job scheduling領域又一個開源利器,可以到http://www.opensymphony.com/quartz查看詳細資料。Quartz是輕量級的組件,開發人員只需要載入單獨的jar包就可以利用Quartz強大的排程功能。當然,假如你為Quartz配備了資料庫持久化任務的特性,Quartz也可以很好的利用這一點,從而在機器重啟後還能夠記住你原先安排的計劃。

    Quartz中我們接觸最多的介面使Scheduler介面,該介面的提供了計劃安排的功能,比如schedule/unschedule計劃、start/pause/stop Scheduler.

    Quartz提供一些常用的Listener(JobListener,TriggerListener,SchedulerListener)用於完全的監視計劃安排和執行情況。

開始我們的Quartz之旅
 HelloWorld example:
想必大家很想看一個HelloWorld的例子了吧,那麼還是以HellowWorld開始。

import java.util.Date; import org.quartz.Job; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; /**  * @author snowway  * @version $Id$  */ public class SayHelloWorldJob implements Job{     /*      * (non-Javadoc)      *      * @see org.quartz.Job#execute(org.quartz.JobExecutionContext)      */     public void execute(JobExecutionContext context) throws JobExecutionException{         System.out.println("hello world!");     }

     public static void main(String[] args) throws Exception{         SchedulerFactory factory = new StdSchedulerFactory();         Scheduler scheduler = factory.getScheduler();

         JobDetail jobDetail = new JobDetail("SayHelloWorldJob",                 Scheduler.DEFAULT_GROUP,                 SayHelloWorldJob.class);

         Trigger trigger = new SimpleTrigger("SayHelloWorldJobTrigger",                 Scheduler.DEFAULT_GROUP,                 new Date(),                 null,                 0,                 0L);         scheduler.scheduleJob(jobDetail, trigger);         scheduler.start();     } }

為了簡單起見,我把main方法寫在SayHelloWorldJob中了,執行SayHelloWorldJob可以看到控制台列印hello world.
 回顧Hello World example:
 Job是什嗎? 介面Job是每個業務上需要執行的任務需要實現的介面,該介面只有一個方法:

 package org.quartz;

    public interface Job {

      public void execute(JobExecutionContext context)        throws JobExecutionException;    }

 execute方法也就是當時間到達後,Quartz回調的方法,我們使SayHelloWorldJob實現Job介面以提供列印功能

 JobDetail是什嗎? JobDetail描述了一個任務具體的資訊,比如名稱,組名等等。 JobDetail jobDetail = new JobDetail("SayHelloWorldJob",                 Scheduler.DEFAULT_GROUP,                 SayHelloWorldJob.class); 在上面的構造方法中,第一個是任務的名稱,第二個是組名,第三個就是實際當任務需要執行的回調類。

 Trigger是什嗎? Trigger顧名思義就是觸發器,Quartz有個很好的想法就是分離了任務和任務執行的條件。Trigger就是控制任務執行條件的類,當Trigger認為執行條件滿足的時刻,Trigger會通知相關的Job去執行。分離的好處是: 1.你可以為某個Job關聯多個Trigger,其中任何一個條件滿足都可以觸發job執行,這樣可以完成一些組合的進階觸發條件 2.當Trigger失效後(比如:一個永遠都不能滿足的條件),你不必去聲明一個新的job,代替的是你可以為job關聯一個新的Trigger讓job可以繼續執行。

 目前的Quartz實現中,存在兩種Trigger,SimpleTrigger和CronTrigger,SimpleTrigger用來完成一些比如固定時間執行的任務,比如:從現在開始1分鐘後等等;而CronTrigger(沒錯,和unix的cron進程的含意一樣)用來執行calendar-like的任務,比如:每周五下午3:00,每月最後一天等等。

 Trigger trigger = new SimpleTrigger("SayHelloWorldJobTrigger",                 Scheduler.DEFAULT_GROUP,                 new Date(),                 null,                 0,                 0L); 這個構造方法中,第一個是Trigger的名稱,第二個是Trigger的組名,第三個是任務開始時間,第四個是結束時間,第五個是重複 次數(使用SimpleTrigger.REPEAT_INDEFINITELY常量表示無限次),最後一個是週期(單位是毫秒),那麼這樣就建立 了一個立刻並只執行一次的任務。

 scheduler.scheduleJob(jobDetail, trigger); 這條語句就是把job和Trigger關聯,這樣當Trigger認為應該觸發的時候就會調用(實際上是Scheduler調用)job.execute方法了。

 scheduler.start(); 千萬別忘了加上上面的語句,這條語句通知Quartz使安排的計劃生效。

 關於execute方法的參數JobExecutionContext JobExecutionContext就和很多Context結尾的類功能一樣,提供的運行時刻的上下文環境,JobExecutionContext中有 Scheduler,JobDetail,Trigger等很多個物件的引用,從而當你在execute方法內部須需要這些對象的時刻提供的便利。

 JobDetail和Trigger的name和group Scheduler執行個體對應了很多job和trigger的執行個體,為了方便的區分,Quartz使用name和group這兩個特性,正如你想向的一樣, 同一個group下不能有兩個相同name的JobDetail,Trigger同理 同一個Scheduler下不能有兩個相同group的JobDetail,Trigger同理 JobDetail和Trigger的完全限定名為:group + name
 更深入的思考...
HelloWorld的例子還不足以說明一些問題,一些人可能會這樣問:假如execute方法中需要一些額外的資料怎麼辦?比如說execute 中希望發送一封郵件,但是我需要知道郵件的寄件者、接收者等資訊?

 存在兩種解決方案: 1.JobDataMap類:   每個JobDetail都關聯了一個JobDataMap執行個體,JobDataMap是java.util.Map的子類,基本上是提供key-value形式的資料,並提供了一些便利方法(主要是對java基礎資料型別 (Elementary Data Type)的支援,如put(String key,int value)),當開發人員建立JobDetail的時候,可以把附加資訊放到JobDataMap中,那麼在execute方法中可以根據key找到需要的值。   JobDetail job = new JobDetail....   job.getJobDataMap().put("from","snowway@vip.sina.com");   ...  

在execute中   String from = jobExecutionContext.getJobDetail().getJobDataMap().getString("from");   ....

   不過,當你使用資料庫儲存JobDetail的時候(預設情況下使用RAM),這裡有一個致命的弱點,你不能把沒有實現java.io.Serializable的對象放入JobDataMap中,因為Quartz將使用Blob欄位儲存(也可以通過設定檔關閉)序列化過的JobDataMap中的對象。比如你在execute方法中需要一個java.sql.Connection介面執行個體,這種情況也是普遍的,那麼通常情況下你不能把Connection放入JobDataMap,即使你只想在execute中使用。(註:讀者可暫時認為上面這段話是正確的,然而可以通過指示quartz改變這種行為,那屬於進階話題)

 2.假如你需要一個java.sql.Connection,用於在execute中完成某些操作,那麼你可以把Connection放入Quartz的SchedulerContext中,execute也可以訪問,並且Quartz不會持久化SchedulerContext中的任何東西。

   scheduler.getContext().put("java.sql.Connection",connection);  

 execute中   Connection con = (Connection)jobExecutionContext.getScheduler().getContext().get("java.sql.Connection");

 未完待續...


相關文章

Cloud Intelligence Leading the Digital Future

Alibaba Cloud ACtivate Online Conference, Nov. 20th & 21st, 2019 (UTC+08)

Register Now >

Starter Package

SSD Cloud server and data transfer for only $2.50 a month

Get Started >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。