1. 程式人生 > >Quartz任務排程器詳解

Quartz任務排程器詳解

個人理解:

總共三步走:

1,一個java類去實現Job,重新裡面execute方法,execute()方法裡面為要觸發的邏輯

2,一個觸發器,定義何時執行

3,Job例項化一個JobDetail 物件和觸發器一起組成一個任務,註冊到scheduler 裡面 scheduler.scheduler(JobDetial,job)

主要概念:

1、Job 表示一個工作,要執行的具體內容。此介面中只有一個方法 void execute(JobExecutionContext context) 2、JobDetail JobDetail表示一個具體的可執行的排程程式,Job是這個可執行程排程程式所要執行的內容,另外JobDetail還包含了這個任務排程的方案和策略。
3、Trigger代表一個排程引數的配置,什麼時候去調。 4、Scheduler代表一個排程容器,一個排程容器中可以註冊多個JobDetail和Trigger。當Trigger與JobDetail組合,就可以被Scheduler容器排程了

參考文章:http://blog.sina.com.cn/s/blog_4d36e1ae0100tost.html


價值文章分享:http://wenku.baidu.com/link?url=UxdYEtPKTR7eVa-JBR21rpl2M_4lTed3n0varjo-sDH4WAAlJWZtB9_9TJroPGwFtJpctBTAVhogtDt9WD-qLiAfOL01Et-0IonZaj3rDum


介紹Quartz
Quartz是一個開源的任務排程系統,它能用來排程很多工的執行。

執行環境
Quartz 能嵌入在其他應用程式裡執行。
Quartz 能在一個應用伺服器裡被例項化(或servlet容器), 並 且參與XA事務
Quartz能獨立執行(通過JVM),或者通過RMI
Quartz能被叢集例項化
任務排程
當一個指定給任務的觸發器發生時,任 務就被排程執行. 觸發器能被建立為:

一天的 某個時間(精確到毫秒級)
一週的 某些天
一個月 的某些天
一年的 某些天
不在一 個Calendar列出的某些天 (例 如工作節假日)
在一個 指定的次數重複
重複到 一個指定的時間/日期
無限重 復
在一個 間隔內重複
能夠給任務指定名稱和組名.觸 發器也能夠指定名稱和組名,這樣可以很好的在排程器裡組織起來.一個加入到排程器裡的任務可以被多個觸發器註冊。在J2EE環境裡,任務能作為一個分散式(XA) 事務的一部分來執行。

任務執行
任務能 夠是任何實現Job介面的Java類。
任務類 能夠被Quartz例項化,或者被 你的應用框架。
當一個 觸發器觸發時,排程器會通知例項化了JobListener 和TriggerListener 介面的0個或者多個Java物件(監聽器 可以是簡單的Java物件, EJBs, 或JMS釋出者等). 在任務執行後,這些監聽器也會被通知。
當任務 完成時,他們會返回一個JobCompletionCode ,這 個程式碼告訴排程器任務執行成功或者失敗.這個程式碼也會指示排程器做一些動作-例如 立即再次執行任務。
任務持久化
Quartz的設計包含JobStore介面,這個介面能被實現來 為任務的儲存提供不同的機制。
應用JDBCJobStore, 所有被配置成“穩定”的任務和觸發器能通過JDBC儲存在關係資料庫裡。
應用RAMJobStore, 所有任務和觸發器能被儲存在RAM裡因此不必在程式重起之間儲存-一個好處就是不必使用資料庫。
事務
使用JobStoreCMT(JDBCJobStore的子類),Quartz 能參與JTA事務。
Quartz 能管理JTA事務(開始和 提交)在執行任務之間,這樣,任務做的事就可以發生在JTA事務裡。
叢集
Fail-over.
Load balancing.
監聽器和外掛
通過實 現一個或多個監聽介面,應用程式能捕捉排程事件來監控或控制任務/觸發器 的行為。
外掛機 制可以給Quartz增加功能,例如保持任務執行的歷史記錄,或從一個定義好的檔案里加載任務和觸發器。
Quartz 裝配了很多外掛和監聽器。
1.使用Quartz
在我們用排程器之前,排程器需要例項化。我們用SchedulerFactory 來例項它。一旦排程器被例項,我們就可以啟動它,置它為stand-by模式,最後關閉它。注意:一旦一個排程器被關閉了,如果我們不重新例項化它,它就不可能被再次啟動。直到排程器啟動了或者當排程器處於暫停狀 態,觸發器才能夠觸發。下面有個簡單的例子:

SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();

Scheduler sched = schedFact.getScheduler();

sched.start();

JobDetail jobDetail = new JobDetail(“myJob”,

null,

DumbJob.class);

Trigger trigger = TriggerUtils.makeHourlyTrigger(); // 每個小時觸發

trigger.setStartTime(TriggerUtils.getEvenHourDate(new Date())); // 在下個小時開始

trigger.setName(“myTrigger”);

sched.scheduleJob(jobDetail, trigger);

就象你看到的,使用Quartz是很簡單的。在下一節我們介紹Jobs和Triggers。

2.Jobs 和 Triggers
就象以前提到的,一個實現了Job介面的Java類就能夠被排程器執行。介面如下:

package org.quartz;

public interface Job {

public void execute(JobExecutionContext context) throws JobExecutionException;

}

很簡的,當Job的trigger觸發時,Job的execute(..)方法就會被排程器呼叫。被傳遞到這個方法裡來的 JobExecutionContext物件提供了帶有job執行時的資訊:執行它的排程器控制代碼、觸發它的觸發器控制代碼、job的JobDetail物件和一些其他的項。

JobDetail物件是Job在被加到排程器裡時所建立的,它包含有很多的Job屬性設定,和JobDataMap一樣,可以用來儲存job例項時的一些狀態資訊。

Trigger物件是用來觸發執行Job的。當排程一個job時,我們例項一個觸發器然後調整它的屬性來滿足job執行的條件。觸發器也有一個和它相關的JobDataMap,它是用來給被觸發器觸發的job傳引數的。Quartz有一些不同的觸發器型別,不過,用得最多的是SimpleTrigger和CronTrigger。

如果我們需要在給定時刻執行一次job或者在給定時刻觸發job隨後間斷一定時間不停的執行的話,SimpleTrigger是個簡單的解決辦法;如果我們想基於類似日曆排程的觸發job的話,比如說,在每個星期五的中午或者在每個月第10天的10:15觸 發job時,CronTrigger是很有用的。

為什麼用jobs和triggers呢?很多工排程器並沒有任務和觸發器的概念,一些任務排程器簡單定義一個“job”為在一個執行時間伴隨一些小任務標示,其他的更像Quartz裡job和trigger物件的聯合體。在開發Quartz時,開發者們決定,在排程時間表和在這上面執行的工作應該分開。這是很有用的。

例如,job能夠獨立於觸發器被建立和儲存在任務排程器裡,並且,很多的觸發器能夠與同一個job關聯起來。這個鬆耦合的另一個好處就是在與jobs關聯的觸發器終止後,我們能夠再次配置保留在排程器裡的jobs,這樣的話,我們能夠再次排程這些jobs而不需要重新定義他們。我們也可以在不重新定義一個關聯到job 的觸發器的情況下,修改或替代它。

當Jobs和triggers被註冊到Quartz的排程器裡時,他們就有了唯一標示符。他們也可以被放到“groups”裡,Groups是用來組織分類jobs和triggers的,以便今後的維護。在一個組裡的job和trigger的名字必須是唯一的,換句話說,一個job和trigger 的全名為他們的名字加上組名。如果把組名置為”null”,系統會自動給它置為Scheduler.DEFAULT_GROUP

現在,我們大概有了一些jobs和triggers的理解,隨後2節我們將根多的瞭解它們。

3.更多關於Jobs & JobDetails
Jobs很容易實現,這兒有更多我們需要理解的東西:jobs的本質,job介面的execute(..)方法,關於JobDetails。

當我們實現的一個class是真正的”job”時,Quartz需要知道各種job有的屬性,這是通過JobDetail類做到的。在沒用JobDetail之前,JobDetail的功能的實現是通過在每個job的實現類上加上所有的現在JobDetail的get方法來實現的。這就在每個job類上強加了一些實現一樣功能的程式碼,就顯得每個job類很笨重,於是,Quartz開發者們就創造了JobDetail類。

現在,我們來討論一下在Quartz裡job的本質和job例項的生命週期。首先我們來看看第一節的程式碼片段:

JobDetail jobDetail = new JobDetail(“myJob”,      // job 名稱

sched.DEFAULT_GROUP, // job組名(可以寫’null’來用default group)

DumbJob.class);         //要執行的java類

Trigger trigger = TriggerUtils.makeDailyTrigger(8, 30);

trigger.setStartTime(new Date());

trigger.setName(“myTrigger”);

sched.scheduleJob(jobDetail, trigger);

現在我們定義“DumbJob”類:

public class DumbJob implements Job {

public DumbJob() {

}

public void execute(JobExecutionContext context)

throws JobExecutionException

{

System.err.println(“DumbJob is executing.”);

}

}

可以看到我們給排程器一個JobDetail例項,並且,它通過job的類程式碼引用這個job來執行。每次排程器執行job時,它會在呼叫job的execute(..)方法之前建立一個他的例項。這就帶來了兩個事實:一、 job必 須有一個不帶引數的構造器,二、在job類裡定義資料成員並沒有意義,因為在每次job執 行的時候他們的值會被覆蓋掉。

你可能現在想要問“我怎樣給一個job例項提供屬性/配置?”和“在幾次執行間我怎樣能跟蹤job的狀態?”這些問題的答案是一樣的:用JobDataMap- JobDetail物件的一部分。

JobDataMap
JobDataMap能夠支援任何序列化的物件,當job執行時,這些物件能夠在job例項中可用。JobDataMap實現了Java Map介面,它有一些附加的方法,這些方法用來儲存和跟蹤簡單型別 的資料。

如下程式碼可以很快地給job增加JobDataMap:

jobDetail.getJobDataMap().put(“jobSays”, “Hello World!”);

jobDetail.getJobDataMap().put(“myFloatValue”, 3.141f);

jobDetail.getJobDataMap().put(“myStateData”, new ArrayList());

在job執行時,我們可以在job裡 通過如下程式碼得到JobDataMap:

public class DumbJob implements Job {

public DumbJob() {

}

public void execute(JobExecutionContext context)

throws JobExecutionException

{

String instName = context.getJobDetail().getName();

String instGroup = context.getJobDetail().getGroup();

JobDataMap dataMap = context.getJobDetail().getJobDataMap();

String jobSays = dataMap.getString(“jobSays”);

float myFloatValue = dataMap.getFloat(“myFloatValue”);

ArrayList state = (ArrayList)dataMap.get(“myStateData”);

state.add(new Date());

System.err.println(“Instance ” + instName + ” of DumbJob says: ” + jobSays);

}

}

如果用一個持久JobStore(在指南JobStore章節討論),我們就應該注意在JobDataMap裡放些什麼,因為在它裡面的物件將會被序列化,並且這些物件會因此產生一些class-versioning問題。明顯的,標準Java型別應該是很安全的,但是,任何時候某人改變了一個你已經序列化的例項的類的定義時,我們就要注意不能夠破壞相容性了。在這個方面的進一步資訊 可以在Java Developer Connection Tech Tip: Serialization In The Real World裡找到。我們能把JDBC-JobStore和JobDataMap放到一個模式裡,在那裡,只有簡單型別和String型能被儲存在Map 裡, 從而消去任何以後的序列化問題。

Stateful vs. Non-Stateful Jobs

觸發器也有與它們關聯的JobDataMaps。假設我們有一個儲存在排程器裡被多個觸發器關聯的job,然而,對於每個獨立的觸發器,我想提供給job不同的資料輸入,在這個時候,JobDataMaps就很有用了。

在job執行期間,JobDataMaps能夠在JobExecutionContext裡獲得。JobDataMap融合在Trigger和JobDetail類裡,JobDataMap裡面的值能夠利用key來更新。

以下例子顯示,在job執行期間從JobExecutionContext裡的JobDataMap得到資料:

public class DumbJob implements Job {

public DumbJob() {

}

public void execute(JobExecutionContext context)

throws JobExecutionException

{

String instName = context.getJobDetail().getName();

String instGroup = context.getJobDetail().getGroup();

JobDataMap dataMap = context.getJobDataMap();  // 注意:不同於以前的例子

String jobSays = dataMap.getString(“jobSays”);

float myFloatValue = dataMap.getFloat(“myFloatValue”);

ArrayList state = (ArrayList)dataMap.get(“myStateData”);

state.add(new Date());

System.err.println(“Instance ” + instName + ” of DumbJob says: ” + jobSays);

}

}

StatefulJob
現在,關於job狀 態資料的一些附加要點:一個job例項能定義為”有 狀態的”或者”無狀態的”。無狀態的jobs僅當它們在被加入到排程器裡時才儲存JobDataMap。這就意味著,在jobs執行期間對JobDataMap裡資料的任何改變都會丟失,下次執行時job將 看不到這些資料。你可能會猜到,一個有狀態的job就是它的反面例子-它的JobDataMap是在每次執行完job後再次儲存的。一個缺點就是有狀態的job不能夠併發執行。換句話說,如果job是有狀態的,一個觸發器嘗試觸發這個已經執行了的job時,這個觸發器就會等待直到這次執行結束。

用實現StatefulJob 接 口來標記一個job是有狀態的。

Job ‘Instances’
我們能夠建立一個單獨的job類,並且通過建立多個JobDetails例項在排程器裡儲存很多它的“例項定義”,每個都有它自己的屬性集和JobDataMap ,把它們都加入到排程器裡。

當一個觸發器觸發時,與它關聯的job就 是通過配置在排程器上的JobFactory 來例項化的。預設的JobFactory 簡單的呼叫在job類上的newInstance()方法,你可能想要建立自己的JobFactory實現來完成一些自己想要的事情,如:擁有應用程式的 IoC或 者DI容器程序/初始化job實 例。

job的其他屬性
這兒有一個其他屬性的總結,這些屬性是通 過JobDetail物件為一個job例項定義的。

永續性– 如果一個job是 非持久的,一旦沒有任何可用的觸發器與它關聯時,他就會自動得從排程器裡被刪除。
不穩定 性-如果一個job是 不穩定的,他就不會在重起Quartz排程器之間持久化。
請求恢 復– 如果一個job“請 求恢復”,在排程器“硬關閉”(如:該程序崩潰,機器被關掉)時這個job還在執行,過後,當排程器再次啟動時,他就會再次執行。在這種情況下,JobExecutionContext.isRecovering() 方法將會返回true.
Job監 聽器 –一個job能夠有0個或者多個與它關聯的監聽器。當job執行時,監聽器就會被通知。在監聽器的更多討論請看TriggerListeners & JobListeners
JobExecutionException
最後,我們來看看Job.execute(..)方法的一些細節。你能夠從execute方法裡丟擲的僅有的異常型別就是 JobExecutionException。因為這樣,我們應該使用try-catch塊包圍整個execute方法內容。我們還應該花一些時間看看 JobExecutionException文件。當job執行發生異常時,通過設定JobExecutionException,可以讓此job再 次進入排程器或者今後不再執行。

4.更多關於Triggers
象jobs一樣,triggers也相對來說很容易。但是,我們還是要理解它的一些特性。Quartz裡也有很多型別的trigger提供給我們使用。

Calendars
Quartz Calendar 物件(不是java.util.Calendar物件)能夠在trigger儲存在排程器時和trigger關聯起來。Calendars主要用來在 trigger配置時排除一些時間。例如,你能夠建立一個在每個工作日早上9:30觸發的trigger,然後為這個trigger增加一個排除所有商業的節假日的Calendar。

Calendars能夠是任何序列化的物件,只要這些物件實現了Calendar介面:

package org.quartz;

public interface Calendar {

public boolean isTimeIncluded(long timeStamp);

public long getNextIncludedTime(long timeStamp);

}

注意到這些方法的引數型別是long。這意味著calendars能夠排除毫秒級的時間段。大部分地,我們感興趣的是一整天的,所以在Quartz裡,有個實現類提供了方便:org.quartz.impl.HolidayCalendar

Calendars必須被例項化並且通過addCalendar(..)方法註冊到排程器裡。如果你用HolidayCalendar,在例項它之後,你應該用它的addExcludedDate(Date date)方法以便組裝上你想排除的那幾天。一個calendar例項能夠被多個triggers使用:

HolidayCalendar cal = new HolidayCalendar();

cal.addExcludedDate( someDate );

sched.addCalendar(“myHolidays”, cal, false);

Trigger trigger = TriggerUtils.makeHourlyTrigger(); // 每小時觸發

trigger.setStartTime(TriggerUtils.getEvenHourDate(new Date()));  //下一個小時開始  trigger.setName(“myTrigger1″);

trigger.setCalendarName(“myHolidays”);

// .. schedule job with trigger

Trigger trigger2 = TriggerUtils.makeDailyTrigger(8, 0); // 每天早上8點觸發

trigger2.setStartTime(new Date()); //立即開始

trigger2.setName(“myTrigger2″);

trigger2.setCalendarName(“myHolidays”);

// .. schedule job with trigger2

不觸發(misfire)指令
觸發器的另外一個重要的屬性是“不觸發指令”。如果一個持久的觸發器由於排程器被關閉了而沒有找到它的觸發時間,那麼一個不觸發將會發生。不同的觸發器型別有不同的不觸發指令。預設的,他們都用“smart policy”指令-這是一個基於觸發器型別和配置的動態行為。當排程器啟動時,他將會搜尋所有沒觸發的持久化的triggers,然後基於他們各個配置的不觸發指令來更新他們。當你用Quartz,你應該熟悉各個不觸發指令,我們在以下章節有一些介紹。給一個trigger例項配置不觸發指令,要用此例項的setMisfireInstruction(..)方法。

TriggerUtils – Triggers Made Easy
TriggerUtils類(在org.quartz包裡)包含了很多方便的工具。能夠幫你建立triggers和datas。用這個類能夠很容易製造一些trigges,這些triggers能夠在每分鐘,每小時,每週,每個月等等觸發。用它也能產生一些接近某個秒、分鐘、小時的天-這在設定trigger的啟動時間很有幫助。

TriggerListeners
最後,triggers有一些註冊了的監聽器,象job一樣。實現了TriggerListener介面的物件將接受一個trigger被觸發的通知。

5. SimpleTrigger
詳細介紹一下它的構造器:

public SimpleTrigger(String name, //trigger名稱

String group, //trigger的組名

Date startTime, //開始時間

Date endTime, //結束時間

int repeatCount, //重複次數

long repeatInterval)//重複間隔

舉幾個常用例子:

從現在開始10秒後執行一次:

long startTime = System.currentTimeMillis() + 10000L;

SimpleTrigger trigger = new SimpleTrigger(“myTrigger”,

null,

new Date(startTime),

null,

0,

0L);

立即執行,60秒間隔無限制重複:

SimpleTrigger trigger = new SimpleTrigger(“myTrigger”,

null,

new Date(),

null,

SimpleTrigger.REPEAT_INDEFINITELY,

60L * 1000L);

從現在開始立即執行,每10秒重複,直到40秒 後:

long endTime = System.currentTimeMillis() + 40000L;

SimpleTrigger trigger = new SimpleTrigger(“myTrigger”,

“myGroup”,

new Date(),

new Date(endTime),

SimpleTrigger.REPEAT_INDEFINITELY,

10L * 1000L);

在2002年3月17號10:30am觸發,重複5次(一共6次),30秒 間隔:

java.util.Calendar cal = new java.util.GregorianCalendar(2002, cal.MARCH, 17);

cal.set(cal.HOUR, 10);

cal.set(cal.MINUTE, 30);

cal.set(cal.SECOND, 0);

cal.set(cal.MILLISECOND, 0);

Data startTime = cal.getTime();

SimpleTrigger trigger = new SimpleTrigger(“myTrigger”,

null,

startTime,

null,

5,

30L * 1000L);

SimpleTrigger 不觸發指令
MISFIRE_INSTRUCTION_FIRE_NOW

MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT

MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT

MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT

MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT

6.CronTrigger
構造器
CronTrigger(String name, //觸發器名稱

String group, //觸發器的組名

String jobName, //job名稱

String jobGroup, //job的組名

Date startTime, //開始時間

Date endTime, //結束時間

String cronExpression, //克隆表示式

TimeZone timeZone)//時區

還有一些其它引數少一些的構造器,參考JavaDoc。通常我們如下簡單地使用CronTrigger;

Trigger trigger = new CronTrigger(“trigger1″, “group1″);//設定觸發器名稱和組名

trigger.setCronexpression_r(“0 0 15 * * ?”);//設定克隆表示式

克隆表示式
一個克隆表示式是一個由空白間隔6個或者7個欄位的字串。

格式:

欄位名 必須有? 值範圍 允許的特殊字元
Seconds YES 0-59 , – * /
Minutes YES 0-59 , – * /
Hours YES 0-23 , – * /
Day of month YES 1-31 , – * ? / L W C
Month YES 1-12 or JAN-DEC , – * /
Day of week YES 1-7 or SUN-SAT , – * ? / L C #
Year NO empty, 1970-2099 , – * /

例子:

* * * * ? *

0 0/5 14,18,3-39,52 ? JAN,MAR,SEP MON-FRI 2002-2010

特殊字元
* 表示所有值 ;
? 表示未說明的值,即不關心它為何值;
- 表示一個指定的範圍;
, 表示附加一個可能值;
/ 符號前表示開始時間,符號後表示每次遞增的值;
L (“last”) “L” 用在day-of-month欄位意思是 “這個月最後一天“;用在 day-of-week欄位, 它簡單意思是 “7″ or “SAT”。 如果在day-of-week欄位裡和數字聯合使用,它的意思就是 “這個月的最後一個星期幾“ – 例如: “6L” means “這個月的最後一個星期五“. 當我們用“L”時,不指明一個列表值或者範圍是很重要的,不然的話,我們會得到一些意想不到的結果。
W (“weekday”) –只能用在day-of-month欄位。用來描敘最接近指定天的工作日(週一到週五)。例如:在day-of-month欄位用“15W” 指“最接近這個月第15天的工作日”,即如果這個月第15天是週六,那麼觸發器將會在這個月第14天即週五觸發;如果這個月第15天是週日,那麼觸發器將會在這個月第16天即週一觸發;如果這個月第15天是週二,那麼就在觸發器這天觸發。注意一點:這個用法只會在當前月計算值,不會越過當前月。“W”字元僅能在day-of-month指明一天,不能是一個範圍或列表。
也可 以用“LW”來指 定這個月的最後一個工作日。

# -只能用在day-of-week欄位。用來指定這個月的第幾個周幾。例:在day-of-week欄位用”6#3″指這個月第3個週五(6指周 五,3指第3個)。 如果指定的日期不存在,觸發器就不會觸發。
C (“calendar”) – 指和calendar聯絡後計算過的值。例:在day-of-month 欄位用“5C”指在這個月第5天或之後包括calendar的第一天;在day-of-week欄位用“1C”指在這週日或之後包括calendar的第一天。
在MONTH和Day of week欄位裡對字母大小寫 不敏感。

一些例子
表示式 意思(觸發時刻)
0 0 12 * * ? 每天中午12點
0 15 10 * * ? 2005 在2005年的每天10:25
0 10,44 14 ? 3 WED 在3月裡每個週三的14:10和14:44
0 15 10 ? * 6L 2002-2005 從2002年到2005年裡,每個月的最後一個星期五的10:15
0 0 12 1/5 * ? 從當月的第一天開始,然後在每個月每隔5天的12:00
0 15 10 ? * 6#3 每個月第3個週五的10:15

注意在day-of-week和day-of-month欄位裡使用“?”和“*”的效 果。

注意
對“C”的支援並不很完全。
對在day-of-week欄位和在day-of-month欄位同時使用也不是很完全(目前你必須在這兩個欄位中的一個用“?”指定)。
當設 置在午夜和凌晨1點 之間觸發時要仔細。
不觸發指令:

MISFIRE_INSTRUCTION_FIRE_ONCE_NOW

MISFIRE_INSTRUCTION_DO_NOTHING

7.TriggerListeners 和JobListeners
與Trigger相關的事件有:觸發器觸發,觸發器的不觸發(參考先前章節),觸發器完成。

public interface TriggerListener {

public String getName();

public void triggerFired(Trigger trigger, JobExecutionContext context);

public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);

public void triggerMisfired(Trigger trigger);

public void triggerComplete(Trigger trigger, JobExecutionContext context,

int triggerInstructionCode);

}

與job相關的事件有:job準備執行,job執行完畢。

public interface JobListener {

public String getName();

public void jobToBeExecuted(JobExecutionContext context);

public void jobExecutionVetoed(JobExecutionContext context);

public void jobWasExecuted(JobExecutionContext context,

JobExecutionException jobException);

}

使用Listeners
建立一個監聽器,就是建立一個實現了org.quartz.TriggerListener 和 org.quartz.JobListener介面的物件。在執行的期間用排程器註冊監聽器,必須要給它提供一個名字。監聽器能夠註冊成為全域性的或者不是全域性的,全域性監聽器接受所有的事件,而非全域性的則僅接受指定給triggers/jobs了的事件。

監聽器是在執行期間被排程器註冊的,他們沒有伴隨jobs和triggers儲存在JobStore裡。Jobs和triggers僅儲存和它們相關的監聽器的名字。因此,每次程式執行時,監聽器需要被排程器再次註冊。

scheduler.addGlobalJobListener(myJobListener);

scheduler.addJobListener(myJobListener);

監聽器在Quartz並不是經常使用的。

8.SchedulerListeners
和排程器相關的事件有:job/trigger的加入和移出,一些排程器裡的錯誤,排程器關閉等等。

public interface SchedulerListener {

public void jobScheduled(Trigger trigger);

public void jobUnscheduled(String triggerName, String triggerGroup);

public void triggerFinalized(Trigger trigger);

public void triggersPaused(String triggerName, String triggerGroup);

public void triggersResumed(String triggerName, String triggerGroup);

public void jobsPaused(String jobName, String jobGroup);

public void jobsResumed(String jobName, String jobGroup);

public void schedulerError(String msg, SchedulerException cause);

public void schedulerShutdown();

}

建立和註冊SchedulerListeners和其他監聽器一樣,全域性和非全域性的沒有區別。

9.JobStores
JobStore負責儲存所有配置到排程器裡的工作資料:jobs,triggers,calendars等等。在用 SchedulerFactory得到一個排程器的例項時,我們可以給SchedulerFactory提供一個屬性檔案或者一個屬性物件來宣告使用哪個 JobStore。

注意,不要在程式碼裡使用JobStore的例項,這些Quartz都做好了。我們要做的就僅僅告訴Quartz(通過配置)用哪個JobStore,然後就呼叫Scheduler介面函數了。

RAMJobStore
利用記憶體來持久化排程程式資訊。這種作業儲存型別最容易配置、構造和 執行,但是當應用程式停止執行時,所有排程資訊將被丟失。

在屬性檔案裡指定:

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

JDBCJobStore
支援的資料庫有:Oracle, MySQL, MS SQLServer2000, HSQLDB, PostreSQL and DB2。使用JDBCJobStore,首先要在資料庫裡建一些Quartz要使用的表。我們可以使用Quartz釋出包裡的建表指令碼,在 docs/dbTables目錄下。如果沒有你所要的資料庫型別的指令碼,可以在已有的指令碼作一些修改。所有這些標都是以“QRTZ_”作為字首的,這個字首是可以在屬性檔案裡更改的。在為多個排程器例項建立多個系列的表時,用不同的 字首是很有用的。

一旦我們建立了這些表,在配置和觸發JDBCJobStore之前就要做更多的事情了。我們需要決定應用需要哪種型別的事務處理。如果我們不需要給其他的事務處理一些排程命令(增加刪除trigger),我們就可以讓Quartz利用JobStoreTX處理這個事務(這用的很多)。

如果我們需要Quartz和其他的事務處理(在J2EE應用伺服器裡)一起工作,我們就應該用JobStoreCMT-這會使Quartz讓應用伺服器容器管理事務。

最後一點是從哪個JDBCJobStore啟動資料庫能夠得到該資料庫的連線。在屬性檔案裡是用一個不同的方法來定義資料來源的。一種是Quartz自己建立和管理資料來源-提供所有的資料庫連線資訊;另外一種是利用應用伺服器管理的資料來源,其中Quartz執行在這個應用伺服器裡-給JDBCJobStore提供資料庫的JNDI名稱。

用JDBCJobStore(假設我們是用的StdSchedulerFactory),我們首先要設定 org.quartz.jobStore.class屬性為org.quartz.impl.jdbcjobstore.JobStoreTX或者 org.quartz.impl.jdbcjobstore.JobStoreCMT,這取決於我們的選擇。

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

下一步,我們需要選擇一個驅動代理。StdJDBCDelegate是一個用“vanilla”JDBC程式碼實現的代理。如果沒有其他為你資料庫指定的代理,就使用這個。Quartz開發者們解決的問題都是根據這個代理的來實現的。其他的代理在 org.quartz.impl.jdbcjobstore包或者子包裡。包括DB2v6Delegate(DB2 version 6 或早期版本使用的),HSQLDBDelegate(HSQLDB使用),MSSQLDelegate(microsoft SQLServer 2000使用),PostgreSQLDelegate(PostgreSQL 7.x使用),WeblogicDelegate(Weblogic的JDBC驅動器使用),OracleDelegate(Oracle 8i and 9i使 用)。

org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

在下一步,我們要配置表的字首:

org.quartz.jobStore.tablePrefix = QRTZ_

最後,我們需要設定用哪個資料來源,資料來源的名稱必須在Quartz屬性裡定義好。例如,我們可以給Quartz指定使用“myDS”(在配置屬性裡的其他地方定義好了)作為資料來源的名字。

org.quartz.jobStore.dataSource = myDS

如果排程器很繁忙(例如,執行job的個數和執行緒池的大小一樣),那麼我們應該設定資料來源的連線個數線上 程池大小+1之 上。

org.quartz.jobStore.useProperties這個屬效能夠設定為“true” (預設為false),用來指示JDBCJobStore:在JobDataMaps裡的所有值都應該是String,這樣在能作為name-value方式儲存,而不是在BLOB列裡以序列化的格式儲存複雜的物件。從長遠看,這樣做會很安全,因為你可以避免將非String的類序列化到BLOB裡 的類版本問題。

10.配置,資源使用和排程器工廠
Quartz是以標準組件的方式組織的,所以,使它執行起來,一些元件 需要被聯合起來。

在Quartz能夠工作之前,需要配置的主要元件有:

執行緒池
作業儲 存
資料來源(需要的話)
排程器 自己
在執行jobs時,執行緒池為Quartz提供了一系列的執行緒。線上程池裡的執行緒越多,能夠並行執行的jobs就越多。但是,太多的執行緒會使系統癱瘓。大部分的Quartz使用者發現,5個執行緒就足夠了-因為他們在指定時間裡只有少於100的jobs,這些jobs並不都是在同一時刻執行,jobs完成得也很快的。其他的使用者發現他們需要10、15、50或者100個執行緒-因為他們在不同的排程器裡用了上萬個觸發器,在給定的時間裡,平均在10到 100個jobs試著執行。為排程器找到合適的執行緒數量完全依賴於你用排程起來做什麼。不在乎執行緒數量,而要確保你有足夠的執行緒來使jobs執行。如果一個觸發器的觸發時間到來了,可是沒有一個能夠用的執行緒,Quartz將會等到可用執行緒的來臨,然後job將會在幾毫秒後執行。這可能會引起不觸發-如果不在屬性檔案裡給排程器配置“misfire threshold”的話。

執行緒池介面是在org.quartz.spi包裡定義的,你能夠建立一個執行緒池以自己的方法。Quartz裝配了一個簡單(但是很好的)的執行緒池,是org.quartz.simpl.SimpleThreadPool。這個執行緒池簡單的維護一些在池裡固定的執行緒-不會增加也不會減少。但是它能夠做很多事而且經過測試了的,幾乎每個Quartz使用者用這個執行緒池。

JobStores 和 DataSrouces在前面討論過了,這裡值得一提的是,所有JobStores都實現了org.quartz.spi.JobStore介面,如果在打包裡的任何一個JobStore不能夠滿足你的需求的話,你可以自己做一個。

最後,你需要建立你的Scheduler例項。Scheduler需要提供他的名稱,說明RMI的設定,處理JobStore和ThreadPool的例項。RMI設定包括排程器是否作為一個RMI伺服器而建立。StdSchedulerFactory也能夠產生排程器的例項,這些例項實際上是建立在遠端程序中的排程器代理(RMI樁)。

StdSchedulerFactory
StdSchedulerFactory實現了org.quartz.SchedulerFactory介面。它用了一系列的屬性(java.util.Properties)來建立和初始化一個Quartz的排程器。這些屬性通常儲存和載入在一個檔案裡,但是也可以通過你的程式建立直接交給工廠處理。在工廠上呼叫getScheduler()就可以產生排程器,初始化它(還有執行緒池,JobStore和資料來源),然後返回一個控制代碼到這個公共的介面。

// 預設排程器是quartz.propeties檔案定義的,這個檔案可以在當前目錄下找到,也可以在//classpath裡找到,如果都找不到了,就用quartz.jar裡的quartz.propeties檔案。

SchedulerFactory sf = new StdSchedulerFactory();

Scheduler scheduler = sf.getScheduler();

scheduler.start();

用指定的屬性物件初始化:

SchedulerFactory sf = new StdSchedulerFactory();

sf.initialize(schedulerProperties);// schedulerProperties是屬性物件

Scheduler scheduler = sf.getScheduler();

scheduler.start();

用指定的屬性檔案初始化:

SchedulerFactory sf = new StdSchedulerFactory();

sf.initialize(fileName);//屬性檔案全名

Scheduler scheduler = sf.getScheduler();

scheduler.start();

DirectSchedulerFactory
DirectSchedulerFactory是另外的一個SchedulerFactory實現。在更多的程式設計方法裡建立排程器時,他很有用。他的用法不被贊成,原因有:1.它需要使用者更清楚的知道他們在做什麼。2.它不允許配置,就是說,你必須要在程式碼裡配置所有的排程器屬性。

Logging
Quartz給它所有需要的日誌是使用org.apache.commons.logging框架的。Quartz沒有產生很多的日誌資訊。僅有一些在初始化時關於一些jobs正在執行的問題的資訊。為了調整日誌設定,我們需要了解Jakarta Commons Logging框架,超過了本文件討論的範圍。

11.高階(企業)特性
叢集
目前叢集僅以JDBC-Jobstore (JobStoreTX or JobStoreCMT)工作。這些特性包含load-balancing和任務fail-over(如果JobDetail的”request recovery”標誌設為true的話)。

通過設定org.quartz.jobStore.isClustered屬性為“true”來使用叢集。在叢集裡的每個排程器例項應該用一樣的 quartz.properties檔案。叢集會有如下異常:執行緒池大小不同,屬性org.quartz.scheduler.instanceName 值不同。其實在叢集的每個節點都有一個唯一的例項ID,要達到這樣也很簡單,也不需要不同的屬性檔案,只要將屬性org.quartz.scheduler.instanceId的值設定為“AUTO”。

不要在一個分離開的機器上執行叢集,除非他們的時鐘是用時鐘 同步服務同步過的。如果不熟悉怎樣同步,參考:http://www.boulder.nist.gov/timefreq/service/its.htm

其他排程器例項在用資料表時,不要觸發一個也用到這些資料表 的不是叢集的排程器例項。你會得到一些沒用的資料。

JTA 事務
在第9節解釋過JobStores,JobStoreCMT允許Quartz排程一些具有很大JTA事務的操作。

通過設定“org.quartz.scheduler.wrapJobExecutionInUserTransaction”屬性為 true,Jobs也能夠在一個JTA事務裡執行。有了這個設定,一個JTA事務會在job的execute()方法呼叫前開始(begin),然後在呼叫execute()方法結束後提交(commit)。

除了在JTA事 務裡Quartz自動地和job的執行掛鉤之外,當使用JobStoreCMT時也可以呼叫你在排程器接口裡的實現的方法,確保你在呼叫一個排程器上的方法之前開始了事務。你也可以直接自己做,使用UserTransaction,或者把用了排程器的程式碼放在一個使用容器的SessionBean裡來管理事務。

12. Quartz 的其他特性
Plug-Ins
Quartz 提供了一個介面(org.quartz.spi.SchedulerPlugin) 來實現plugging-in 的功能。

裝配給Quartz的Plugins能提供不同的有用的功能。在org.quartz.plugins包裡有詳細說明。他們提供的功能例如:排程器啟動時自動排程jobs,記錄job和triggers事件的歷史,當JVM退出時確保排程器關閉。

可以通過配置屬性檔案來使用自己實現或Quartz自帶的外掛。

JobFactory
當一個trigger觸發時,通過一個配置到排程器上的JobFactory,與trigger相關的job就被例項化了。預設的 JobFactory會在job類上呼叫newInstance(),你可能想要建立自己的JobFactory實現來完成一些其他的事情,如:擁有應用程式的IoC或者DI容器程序/初始化job實 例。

與Scheduler.setJobFactory(fact)方法聯合起來察看org.quartz.spi.JobFactory介面,

Jobs工具
Quartz也提供一些有用的job,你能夠用這些job來發郵件或者呼叫EJB。我們能在org.quartz.jobs包裡找到它們。

13.配置檔案裡配置項總結
設定主要排程器
屬性名 必須 型別 預設值
org.quartz.scheduler.instanceName no string ‘QuartzScheduler’
org.quartz.scheduler.instanceId no string ‘NON_CLUSTERED’
org.quartz.scheduler.threadName no string instanceName + ‘_QuartzSchedulerThread’
org.quartz.scheduler.idleWaitTime no long 30000
org.quartz.scheduler.dbFailureRetryInterval no long 15000
org.quartz.scheduler.classLoadHelper.class no string (class name) org.quartz.simpl.CascadingClassLoadHelper
org.quartz.context.key.SOME_KEY no string none
org.quartz.scheduler.userTransactionURL no string (url) ‘java:comp/UserTransaction’
org.quartz.scheduler.wrapJobExecutionInUserTransaction no booelan false
org.quartz.scheduler.jobFactory.class no string (class name) org.quartz.simpl.SimpleJobFactory

org.quartz.scheduler.instanceName
任意的String,對於排程器自己並沒有意義。但是當多個排程器例項用在一個程式裡時,他就可以用來為客戶端程式碼區別每個排程器。如果你用叢集這個特性,你必須為在叢集裡的每個例項用一樣的名字,實現邏輯上的一樣的排程器。

org.quartz.scheduler.instanceId
任意的String,如果在一個叢集裡多個例項是一個邏輯上一樣的排程器時,每個例項的這項屬性必須唯一。你可以設定這項為“AUTO”從而自動收集ID。

org.quartz.scheduler.idleWaitTime
當排程器空閒時,在再次查詢可用triggers之前,排程器將要等等待的毫秒數。正常情況下,我們不調整這個引數,除非我們用XA事務,或者在立即觸發trigger時結果延誤了。

org.quartz.scheduler.classLoadHelper.class
不需要更改。

org.quartz.context.key.SOME_KEY

設定org.quartz.context.key.MyKey = MyValue等價於scheduler.getContext().put(“MyKey”, “MyValue”)

org.quartz.scheduler.userTransactionURL
是一個JNDI URL,Quartz用它來定位應用伺服器的UserTransaction管理器。Websphere使用者可能需要設定它為“jta/usertransaction”。在Quartz配置用到JobStoreCMT時並且屬性org.quartz.scheduler.wrapJobExecutionInUserTransaction設定為true時才有用。

org.quartz.scheduler.wrapJobExecutionInUserTransaction
設定這項為true使我們在呼叫job的execute()之前能夠開始一個UserTransaction。在job的execute()完成之後,事務將會提交,並且,JobDataMap也更新了(是有狀態的job)。

設定執行緒池
屬性名 必須 型別 預設值
org.quartz.threadPool.class yes string (clas name) null
org.quartz.threadPool.threadCount yes int -1
org.quartz.threadPool.threadPriority no int Thread.NORM_PRIORITY (5)
org.quartz.threadPool.makeThreadsDaemons no boolean false
org.quartz.threadPool.threadsInheritGroupOfInitializingThread no boolean true
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread no boolean false

org.quartz.threadPool.class
通常使用org.quartz.simpl.SimpleThreadPool

org.quartz.threadPool.threadPriority
在 Thread.MIN_PRIORITY (1) 和Thread.MAX_PRIORITY (10)之間

org.quartz.threadPool.makeThreadsDaemons、org.quartz.threadPool.threadsInheritGroupOfInitializingThread 和org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread 三 個屬性是指定的SimpleThreadPool的屬性。

如果用自己實現的執行緒池,可如下配置:

org.quartz.threadPool.class = com.mycompany.goo.FooThreadPool

org.quartz.threadPool.somePropOfFooThreadPool = someValue

設定全域性監聽器
全域性監聽器要有一個無引數的構造器,它的屬性是通過反射設定的,僅支援簡單資料和String。

Trigger監聽器:

org.quartz.triggerListener.NAME.class = com.foo.MyListenerClass

org.quartz.triggerListener.NAME.propName = propValue

org.quartz.triggerListener.NAME.prop2Name = prop2Value

job監聽器:

org.quartz.jobListener.NAME.class = com.foo.MyListenerClass

org.quartz.jobListener.NAME.propName = propValue

org.quartz.jobListener.NAME.prop2Name = prop2Value

設定Plugins
配置自己的外掛(和全域性監聽器差不多):

org.quartz.plugin.NAME.class = com.foo.MyPluginClass

org.quartz.plugin.NAME.propName = propValue

org.quartz.plugin.NAME.prop2Name = prop2Value

也可以配置Quartz實現的外掛:

1.trigger歷史日誌記錄外掛(屬性配置中的{數字}參考JavaDoc):

org.quartz.plugin.triggHistory.class=org.quartz.plugins.history.LoggingTriggerHistoryPlugin

org.quartz.plugin.triggHistory.triggerFiredMessage=

Trigger {1}.{0} fired job {6}.{5} at:{4, date, HH:mm:ss MM/dd/yyyy}

org.quartz.plugin.triggHistory.triggerCompleteMessage =

Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy}

2.從XML檔案中初始化job的外掛(屬性配置中的檔名是載入jobs用到的xml檔案,這個檔案必須在classPath裡):

org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin

org.quartz.plugin.jobInitializer.fileName =data/my_job_data.xml

org.quartz.plugin.jobInitializer.overWriteExistingJobs = false

org.quartz.plugin.jobInitializer.failOnFileNotFound = true

在上例中,JobInitializationPlugin只支援一個xml檔案的初始化,Quartz還提供多個xml檔案的初始化,用JobInitializationPluginMultiple,檔名用“,”隔開。

含有多個Jobs的一個xml檔案的一個例子:

<?xml version=’1.0′ encoding=’utf-8′?>

<quartz xmlns=”http://www.opensymphony.com/quartz/JobSchedulingData”

xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=”http://www.opensymphony.com/quartz/JobSchedulingData

http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd”

version=”1.5″>

<calendar class-name=”org.quartz.impl.calendar.HolidayCalendar” replace=”true”>

<name>holidayCalendar</name>

<description>HolidayCalendar</description>

<base-calendar class-name=”org.quartz.impl.calendar.WeeklyCalendar”>

<name>weeklyCalendar</name>

<description>WeeklyCalendar</description>

<base-calendar class-name=”org.quartz.impl.calendar.AnnualCalendar”>

<name>annualCalendar</name>

<description>AnnualCalendar</description>

</base-calendar>

</base-calendar>

</calendar>

<job>

<job-detail>

<name>testJob1</name>

<group>testJobs</group>

<description>Test Job Number 1</description>

<job-class>personal.ruanyang.quartz.plugin.SimpleJob</job-class>

<volatility>false</volatility>

<durability>false</durability>

<recover>false</recover>

<job-data-map allows-transient-data=”true”>

<entry>

<key>test1</key>

<value>test1</value>

</entry>

<entry>

<key>test2</key>

<value>test2</value>

</entry>

</job-data-map>

</job-detail>

<trigger>

<cron>

<name>testTrigger1</name>

<group>testJobs</group>

<description>Test Trigger Number 1</description>

<job-name>testJob1</job-name>

<job-group>testJobs</job-group>

<!–

<start-time>2003-12-17 2:15:00 pm</start-time>

<end-time>2013-12-17 2:15:00 pm</end-time>

–>

<cron-expression>0/15 * * ? * *</cron-expression>

<!– every 15 seconds… –>

</cron>

</trigger>

</job>

<job>

<job-detail>

<name>testJob2</name>

<group>testJobs</group>

<description>Test Job Number 2</description>

<job-class>personal.ruanyang.quartz.plugin.SimpleJob</job-class>

<volatility>false</volatility>

<durability>false</durability>

<recover>false</recover>

</job-detail>

<trigger>

<simple>

<name>testTrigger2</name>

<group>testJobs</group>

<description>Test Trigger Number 2</description>

<calendar-name>holidayCalendar</calendar-name>

<job-name>testJob2</job-name>

<job-group>testJobs</job-group>

<start-time>2004-02-26T12:26:00</start-time>

<repeat-count>10</repeat-count>

<repeat-interval>5000</repeat-interval>

</simple>

</trigger>

</job>

</quartz>

3.Shutdown Hook(通過捕捉JVM關閉時的事件,來關閉排程器)外掛:

org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin

org.quartz.plugin.shutdownhook.cleanShutdown = true

設定RMI
RMI Server Scheduler Properties

沒有必需的主要屬性,所有的都是合理的預設的。通過RMI使用Quartz時,我們需要啟動一個配置好了的Quartz例項來通過RMI“輸出”它的服務。然後我們通過配置Quartz的排程器建立一個客戶端來“代理”它連到伺服器上的工作。

一些使用者在客戶端和伺服器端經歷過類可用性(jobs classes)的問題,為了解決這些問題,我們需要理解RMI的“codebase”和RMI的安全管理。以下資源在這方面會很有用:

RMI和codebase的精彩描敘:http://www.kedwards.com/jini/codebase.html 重要的一點要意識到,codebase是被客戶端使用的。

安全管理的快速資訊:http://gethelp.devx.com/techtips/java_pro/10MinuteSolutions/10min0500.asp

最後讀來自於java API文件的RMISecurityManager:

http://java.sun.com/j2se/1.4.2/docs/api/java/rmi/RMISecurityManager.html

屬性名 需要 預設值
org.quartz.scheduler.rmi.export no false
org.quartz.scheduler.rmi.registryHost no ‘localhost’
org.quartz.scheduler.rmi.registryPort no 1099
org.quartz.scheduler.rmi.createRegistry no ‘never’
org.quartz.scheduler.rmi.serverPort no random
org.quartz.scheduler.rmi.proxy no false

org.quartz.scheduler.rmi.export

如果我們想要Quartz排程器通過RMI輸出服務,那麼我們就把“rmi.export”標誌執為true。

org.quartz.scheduler.rmi.registryHost
能夠找到的RMI註冊的主機(常為“localhost”)。

org.quartz.scheduler.rmi.registryPort
RMI註冊的監聽埠(常為1099).

org.quartz.scheduler.rmi.createRegistry
設定“rmi.createRegistry” 依照我們想要Quartz怎樣建立RMI註冊。如果我們不想Quartz建立一個註冊,就可以用“false”或“never”(如已經有了一個外部的註冊在運行了)。如果我們想先要Quartz嘗試使用一個存在的註冊並且然後返回再建一個,就用“true”或者“as_needed”。如果我們想要 Quartz嘗試建立一個註冊然後返回使用一個存在的,就用“always”。如果註冊被建立,它將會繫結屬性 “org.quartz.scheduler.rmi.registryPort”提供的埠,“org.quartz.rmi.registryHost”應該是主機。

org.quartz.scheduler.rmi.serverPort
Quartz排程器服務將繫結和監聽連線的埠。預設的,RMI服 務將隨機選擇一個埠。

org.quartz.scheduler.rmi.proxy
如果想要連線到遠端的排程器服務,我們就要設定“org.quartz.scheduler.rmi.proxy”為true。然後必需指定一個主機和它註冊了的埠號。

在同一個檔案裡給“org.quartz.scheduler.rmi.export”和 “org.quartz.scheduler.rmi.proxy”同時設定為true並沒有意義。如果你這樣做的話,“export”項會被忽略。如果你沒有通過RMI用Quartz,給這兩項同時設定為false當然也沒有用。

設定RAMJobStore
RAMJobStore用來在記憶體裡儲存排程時的資訊(job,trigger,calendars)。RAMJobStore很快並且是輕量級的,但是當程序終止時所有的資訊都將丟失。

通過設定“org.quartz.jobStore.class”屬性來選用RAMJobStore:

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

RAMJobStore 能夠通過下面的屬性來調整:

屬性名 需要 型別 預設值
org.quartz.jobStore.misfireThreshold no int 60000

org.quartz.jobStore.misfireThreshold
在觸發器被認為沒有觸發之前,排程器能承受一個觸發器再次觸發的一個毫秒級數字。

設定JDBC-JobStoreTX
JobStoreTX是在每次行為(如增加一個job) 之後,通過呼叫commit() (或者 rollback())來管理事務。如果你在一個單機應用裡或者當在一個servlet容器裡用Quartz而且應用沒有用JTA事務時,JDBCJobStore是正確的。

JobStoreTX是通過設定“org.quartz.jobStore.class”屬性來選用的:

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

JobStoreTX能夠通過以下屬性來調整:

屬性名 必須 型別 預設值
org.quartz.jobStore.driverDelegateClass yes string null
org.quartz.jobStore.dataSource yes string null
org.quartz.jobStore.tablePrefix no string “QRTZ_”
org.quartz.jobStore.useProperties no boolean false
org.quartz.jobStore.misfireThreshold no int 60000
org.quartz.jobStore.isClustered no boolean false
org.quartz.jobStore.clusterCheckinInterval no long 15000
org.quartz.jobStore.maxMisfiresToHandleAtATime no int 20
org.quartz.jobStore.dontSetAutoCommitFalse no boolean false
org.quartz.jobStore.selectWithLockSQL no string “SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE”
org.quartz.jobStore.txIsolationLevelSerializable no boolean false

org.quartz.jobStore.driverDelegateClass

org.quartz.impl.jdbcjobstore.StdJDBCDelegate (所有JDBC相容的驅動)
org.quartz.impl.jdbcjobstore.MSSQLDelegate (Microsoft SQL Server和Sybase)
org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.impl.jdbcjobstore.WebLogicDelegate (WebLogic驅動)
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.impl.jdbcjobstore.oracle.WebLogicOracleDelegate (用在Weblogic裡的Oracle驅動)
org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate (用在Weblogic裡的Oracle驅動)
org.quartz.impl.jdbcjobstore.CloudscapeDelegate
org.quartz.impl.jdbcjobstore.DB2v6Delegate
org.quartz.impl.jdbcjobstore.DB2v7Delegate
org.quartz.impl.jdbcjobstore.HSQLDBDelegate
org.quartz.impl.jdbcjobstore.PointbaseDelegate
org.quartz.jobStore.misfireThreshold
同RAM

org.quartz.jobStore.clusterCheckinInterval
影響著核查出失敗例項的速度。

org.quartz.jobStore.dontSetAutoCommitFalse
設定這個屬性為“true”是讓Quartz不去在JDBC連線上呼叫setAutoCommit(false)這個函式。

org.quartz.jobStore.selectWithLockSQL
在“LOCKS”表裡選擇一行並且鎖住這行的SQL語句。預設的語句能夠為大部分資料庫工作。“{0}” 是在執行時你配置的表字首。

org.quartz.jobStore.txIsolationLevelSerializable
設定“true”讓Quartz(當用JobStoreTX或CMT)在JDBC連線上呼叫 setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE)。這可以阻止資料庫在高載入或長時間的事務情況下的鎖超 時。

設定JDBC-JobStoreCMT
JobStoreCMT是依賴與被用Quartz的應用管理著的事務。JTA事務必須在嘗試排程(或解除安裝排程)jobs/triggers之前處在程序中。這允許排程工作成為應用加大事務的一部分。JobStoreCMT實際上需要用到兩個資料來源,一個數據源要連到被應用伺服器管理的事務(通過 JTA), 另外一個數據源的連線在全域性(JTA)事務中並不參加。當應用用JTA事 務(例如通過EJB Session Beans)來執行他們的工作時,JobStoreCMT是正確的。

通過設定 ‘org.quartz.jobStore.class’屬性來選用JobStore:

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT

JobStoreCMT通過以下屬性來調整:

屬性名 必須 型別 預設值
org.quartz.jobStore.driverDelegateClass yes string null
org.quartz.jobStore.dataSource yes string null
org.quartz.jobStore.nonManagedTXDataSource yes string null
org.quartz.jobStore.tablePrefix no string “QRTZ_”
org.quartz.jobStore.useProperties no boolean false
org.quartz.jobStore.misfireThreshold no int 60000
org.quartz.jobStore.isClustered no boolean false
org.quartz.jobStore.clusterCheckinInterval no long 15000
org.quartz.jobStore.maxMisfiresToHandleAtATime no int 20
org.quartz.jobStore.dontSetAutoCommitFalse no boolean false
org.quartz.jobStore.dontSetNonManagedTXConnectionAutoCommitFalse no boolean false
org.quartz.jobStore.selectWithLockSQL no string “SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE”
org.quartz.jobStore.txIsolationLevelSerializable no boolean false
org.quartz.jobStore.txIsolationLevelReadCommitted no boolean false

org.quartz.jobStore.dataSource

對於JobStoreCMT,資料來源需要包含能夠加入JTA(容器管理)事務裡的連線。這就意味著資料來源將在應用伺服器裡被配置和管理,並且,Quartz將通過JNDI獲得一個控制代碼。

org.quartz.jobStore.nonManagedTXDataSource

JobStoreCMT需要一個數據源(以上說的第二個)連到不是容器管理的事務。這個值將是定義在配置屬性檔案的一個數據源名稱,這個資料來源必須包含非CMT的連線,換句話說,就是Quartz直接在連線上呼叫commit()和rollback()。

org.quartz.jobStore.dontSetNonManagedTXConnectionAutoCommitFalse
除了它應用於非TX資料來源管理,其他的和org.quartz.jobStore.dontSetAutoCommitFalse一樣

org.quartz.jobStore.txIsolationLevelReadCommitted
設定“true”,讓Quartz在沒有被管理的JDBC連線上呼叫setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED)。這可以阻止一些資料庫(如DB2)在高載入和長 時間事務的情況下發生的鎖超時。

設定資料來源
如果你用JDBC-JobStore,你將需要一個數據源(或在JobStoreCMT裡要2個)。

資料來源能通過2種 方法配置:

Quartz蒐集所有指定在quartz.properties 檔案 裡的屬性來建立資料來源。
指定一 個定位於管理資料來源的應用伺服器的JNDI,這樣Quartz能用它。
每個定義的資料來源必須有個名字,你為這個資料來源定義的一些屬性必須包含這個名字,象下面的。資料來源的“NAME”可以隨便取,只有當我們把資料來源賦給JDBCJobStore時,這個名字起到標示的作用,其他情況下沒什麼用。

Quartz自己建立資料來源通過以下屬性:

屬性名 必須 型別 預設值
org.quartz.dataSource.NAME.driver yes String null
org.quartz.dataSource.NAME.URL yes String null
org.quartz.dataSource.NAME.user no String “”
org.quartz.dataSource.NAME.password no String “”
org.quartz.dataSource.NAME.maxConnections no int 10
org.quartz.dataSource.NAME.validationQuery no String null

org.quartz.dataSource.NAME.validationQuery
是一個可選的SQL查詢字串,資料來源用它來核查和替代失敗/被 破壞的連線。例如,一個Oracle使用者可能選擇“select table_name from user_tables”-這 是一個決不可能失敗的查詢,除非連線是壞的。

org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver

org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@10.0.1.23:1521:demodb

org.quartz.dataSource.myDS.user = myUser

org.quartz.dataSource.myDS.password = myPassword

org.quartz.dataSource.myDS.maxConnections = 30

引用應用伺服器的資料來源:

屬性值 必須 型別 預設值
org.quartz.dataSource.NAME.jndiURL yes String null
org.quartz.dataSource.NAME.java.naming.factory.initial no String null
org.quartz.dataSource.NAME.java.naming.provider.url no String null
org.quartz.dataSource.NAME.java.naming.security.principal no String null
org.quartz.dataSource.NAME.java.naming.security.credentials no String null

org.quartz.dataSource.NAME.java.naming.factory.initial
JNDI上下文初始化工廠的類名。

org.quartz.dataSource.NAME.java.naming.provider.url
連線到JNDI上下文的URL。

org.quartz.dataSource.NAME.java.naming.security.principal
連線到JNDI上下文的首要使用者。

org.quartz.dataSource.NAME.java.naming.security.credentials
連線到JNDI上下文的使用者驗證密碼。

org.quartz.dataSource.myOtherDS.jndiURL=jdbc/myDataSource

org.quartz.dataSource.myOtherDS.java.naming.factory.initial=

com.evermind.server.rmi.RMIInitialContextFactory

org.quartz.dataSource.myOtherDS.java.naming.provider.url=ormi://localhost

org.quartz.dataSource.myOtherDS.java.naming.security.principal=admin

org.quartz.dataSource.myOtherDS.java.naming.security.credentials=123

設定叢集
叢集可以通過fail-over和load balancing功能給排程器帶來既高可靠性又可伸縮性兩大優點。

叢集目前僅能和JDBC-JobStore(JobStoreTX或JobStoreCMT)一起工作,本質上是讓叢集的每個節點共享一個數據庫來工作的。

Load-balancing是自動出現的,叢集的每個節點儘可能快地觸發job。當一個觸發器觸發時刻到了,第一個將獲取觸發器(並加鎖)的節點就是將要觸發它的節點。

Fail-over是一個節點正在執行一個或多個jobs時失敗了出現的。當一個節點失敗了,其他的節點就會在資料庫裡核查條件和鑑別jobs,這些是節點失敗時記錄到了資料庫的。在恢復節點時,任何標記了恢復(JobDetail裡的”requests recovery”屬性)的jobs將會被再次執行,沒有標記的將會簡單地釋放掉。

通過設定“org.quartz.jobStore.isClustered”屬性來使用叢集。在叢集裡每個例項應該用一樣的 quartz.properties檔案。用到的異常也應該是一樣的:不同執行緒池大小,不同 “org.quartz.scheduler.instanceName”屬性值。每個節點應該用唯一的instanceId。我們可以設定 org.quartz.scheduler.instanceId的值為“AUTO”來達到這個目的。

不要在一個分離開的機器上執行叢集,除非他們的時鐘是用時鐘 同步服務同步過的。如果不熟悉怎樣同步,參考:http://www.boulder.nist.gov/timefreq/service/its.htm

其他例項在用資料表時,不要觸發一個不是叢集的也用這些資料表的實 例。你會得到一些沒用的資料。

#=================================================================# Configure Main Scheduler Properties

#=================================================================org.quartz.scheduler.instanceName = MyClusteredScheduler

org.quartz.scheduler.instanceId = AUTO

#=================================================================# Configure ThreadPool

#=================================================================

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

org.quartz.threadPool.threadCount = 25

org.quartz.threadPool.threadPriority = 5

#=================================================================# Configure JobStore

#=================================================================

org.quartz.jobStore.misfireThreshold = 60000

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate

org.quartz.jobStore.useProperties = false