Quartz 項目應用筆記

jack_gogo 12年前發布 | 67K 次閱讀 Quartz 作業調度框架

Quartz 項目應用筆記

Quartz 是一個強大的企業級 Schedule 工具,也是目前最好的開源 Schedule 工具,最近因為項目的需要,簡單的用到了 Quartz 的一些功能,對項目中使用 Quartz 的一些問題做簡單的記錄。

 

Quartz 的應用中,我們用到了以下的一些東西,ScheduleFactory, Scheduler, Job, JobDetail, Trigger,簡單說明一下他們的用途。

 

SchedulerFactory Scheduler 的工廠,我們可以從中獲得受工廠管理的 Scheduler 對象。

 

SchedulerFactory scheduleFactory = new StdSchedulerFactory();

Scheduler scheduler = scheduleFactory.getScheduler();

 

Scheduler 是一個計劃集,其中可以包含多個 JobDetail Trigger 組成的計劃任務。

我們可以從 SchedulerFactory 中取得 Scheduler

 

接口Job是每個業務上需要執行的任務需要實現的接口,該接口只有一個方法:

 

public interface Job {

   public void execute(JobExecutionContext context)

       throws JobExecutionException;

}

 

我們可以在里面定義我們的 Job 執行邏輯,比如清除過期數據,更新緩存等。

 

JobDetail描述了一個任務具體的信息,比如名稱,組名等等。

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

在上面的構造方法中,第一個是任務的名稱,第二個是組名,第三個就是實際當任務需要執行的回調類。

 

Trigger顧名思義就是觸發器,Quartz有個很好的想法就是分離了任務和任務執行的條件。Trigger就是控制任務執行條件的類,當Trigger認為執行條件滿足的時刻,Trigger會通知相關的Job去執行。分離的好處是:

1.你可以為某個Job關聯多個Trigger,其中任何一個條件滿足都可以觸發job執行,這樣可以完成一些組合的高級觸發條件

2.Trigger失效后(比如:一個永遠都不能滿足的條件),你不必去聲明一個新的job,代替的是你可以為job關聯一個新的Triggerjob可以繼續執行。

 

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

 

在我們項目中,都是一些固定時間的 Job,所以只用到了 SimpleTrigger

Trigger trigger = new SimpleTrigger("SayHelloWorldJobTrigger",Scheduler.DEFAULT_GROUP,new Date(),null,0,0L);

這個構造方法中,第一個是Trigger的名稱,第二個是Trigger的組名,第三個是任務開始時間,第四個是結束時間,第五個是重復次數(使用SimpleTrigger.REPEAT_INDEFINITELY常量表示無限次),最后一個是重復周期(單位是毫秒),那么這樣就創建了一個立刻并只執行一次的任務。

 

但我們定義好了 JobDetailJob,和 Trigger 后,就可以開始 Schedule 一個 Job 了。

 

scheduler.scheduleJob(jobDetail, trigger);

 

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

 

scheduler.start();

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

 

關于execute方法的參數JobExecutionContext

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

 

在項目中,我們把需要執行的 Job 相對應的一些信息放在 JobExecutionContext 中,在 Job 執行的時候可以調用。

 

jobDetail.getJobDataMap().put(userid, id);

 

Job 中,我們可以拿到相關的 Context 信息:

 

jobExecutionContext.getJobDetail().getJobDataMap().getInt(userid);

 

JobDetailTriggernamegroup

Scheduler實例對應了很多jobtrigger的實例,為了方便的區分,Quartz使用namegroup這兩個特性,正如你想向的一樣,同一個group下不能有兩個相同nameJobDetailTrigger同理,同一個Scheduler下不能有兩個相同groupJobDetail,Trigger同理,JobDetailTrigger的完全限定名為:group + name

 

為了讓服務器重啟以后,我們的 Scheduler 信息仍然不丟失,我們通常采用數據庫持久化 Scheduler 的信息。

DBScript Quartz 的下載包中的:quartz-1.6.0\docs\dbTables 下,選擇自己使用的 DB 相應的 Script 導入數據庫就可以了。

在應用中,我們需要配置一個 quartz.properties 才能正常使用 DB。我們可以在 quartz-1.6.0\examples\example10 中找到該文件的樣例,稍作一些修改,就可以放到自己項目源碼的根目錄下使用了。

 

設置 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX 即可啟用基于 JDBC Quartz 信息持久化。

 

根據項目情況設置以下配置信息:

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

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

org.quartz.jobStore.useProperties = false

org.quartz.jobStore.dataSource = myDS

org.quartz.jobStore.tablePrefix = QRTZ_

org.quartz.jobStore.isClustered = false

 

org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver

org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/myapplication

org.quartz.dataSource.myDS.user = root

org.quartz.dataSource.myDS.password =

org.quartz.dataSource.myDS.maxConnections = 5

 

但是光設置了 Database 不夠,我們還需要在 Application 啟動的時候自動啟動 Scheduler 才行,我們只需要簡單的寫一個 Servlet Listener 并在 web.xml 中聲明該 Listener ,在 Servlet 容易啟動的時候,Scheduler 就開始自動執行。

 

public class ScheduleStartListener implements ServletContextListener {

   public void contextInitialized(ServletContextEvent servletContextEvent) {

       try {

          scheduleFactory.getScheduler().start();

       } catch (SchedulerException e) {

          // write log

       }

    }

 

   public void contextDestroyed(ServletContextEvent servletContextEvent) {

       try {

          scheduleFactory.getScheduler().shutdown();

       } catch (SchedulerException e) {

          // write log

       }

    }

}

 

web.xml 里面加入以下配置:

<listener>

   <listener-class>org.agilejava.scheduler.ScheduleStartListener</listener-class>

</listener>

 

以上簡單的記錄了在項目中關于 Quartz 的一些應用,如果有什么新的使用心得,會在后面繼續加入的。

 

quartz的總結

關鍵字: Spring   spring quartz 學習心得    

Quartz 是一個強大的企業級 Schedule 工具,也是目前最好的開源 Schedule 工具。Spring中也集成了quartz的應用,下面就講一下如何在spring中使用quartz

 

spring的配置:

 

xml 代碼

1.<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  

2. <property name="triggers">  

3. <list>  

4.  <ref bean="simpleTrigger"/>  

5.  <ref bean="cornTrigger"/>  

6. </list>  

7. </property>  

8.</bean>  

9.<bean id="schedulerControl" class="com.pheh.scheduler.Schedule">  

10. <property name="scheduler">  

11. <ref bean="schedulerFactoryBean"/>  

12. </property>  

13.</bean>  

14.  

15.<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">  

16. <property name="jobDetail">  

17. <ref bean="methodInvokingJobDetail"/>  

18. </property>  

19. <property name="startDelay">  

20. <value>1000</value>  

21. </property>  

22. <property name="repeatInterval">  

23. <value>3000</value>  

24. </property>  

25.</bean>  

26.<bean id="cornTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">  

27. <property name="jobDetail">  

28. <ref bean="methodInvokingJobDetail"/>  

29. </property>  

30. <property name="cronExpression">  

31. <value>0 0 */1 * * ?</value>  

32. </property>  

33.</bean>  

34.  

Job

org.quartz.Job是一個接口,只定義了void execute(JobExecutionContext context)throws JobExecutionException;一個方法。當定時任務被觸發后,系統會自動調用實現了該接口的方法。在spring中,org.springframework.scheduling.quartz.QuartzJobBean對其進行了封裝,使用了Template Method模式。主要是為了在使用jobDataMap時更加方便。QuartzJobBean有兩個方法,

public final void execute(JobExecutionContext context) throws JobExecutionException

Job接口中定義的,spring在該方法里進行了些處理,將jobDataMap中的值注入到該Job的實現者中

 

protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException

這是一個抽象方法,用戶在擴展了QuartzJobBean后,要自己實現該方法,在其中添加相應的業務邏輯

 

JobDetail

JobDetail描述了一個任務具體的信息。在Spring中,JobDetailBeanJobDetail進行了封裝(繼承了JobDetail)。

private String name;//名稱

private String group = Scheduler.DEFAULT_GROUP;//

private String description;//描述

private Class jobClass;//定時任務觸發時,回調的class,該class要實現Job接口或繼承QuartzJobBean

private JobDataMap jobDataMap;//該任務存儲的數據,在回調的時候也可以使用

private boolean volatility = false;//是否持久化到org.quartz.spi.JobStore

private boolean durability = false;//當該任務完成后,是否還在JobStore中繼續保留該任務

private boolean shouldRecover = false;//當系統重新啟動后,是否再次執行該任務

對于jobDataMap,它是是一個封裝過的Map,使用方法同Map,如

jobDetailBean.getJobDataMap().put(target,value);

如果使用了QuartzJobBean,在使用jobDetailBean時,可將target的值設成QuartzJobBean的子類的屬性名稱,這樣,在定時觸發時,spring會自動將與target對應的value值注入到QuartzJobBean的子類中去。如。

 

 

java 代碼

1....   

2.public class ReminderManager extends QuartzJobBean{   

3. private String reminderStr = "";   

4.}   

5....   

6.jobDetailBean.getJobDataMap().put(reminderStr,"abcdefg");   

7....  

 

這樣當該任務被觸發后,在ReminderManager中,reminderStr的值就會被注入為"abcdefg"

 

Trigger

trigger就是觸發器。Quartz有個很好的想法就是分離了任務和任務執行的條件。Trigger就是控制任務執行條件的類,當Trigger認為執行條件滿足的時刻,Trigger會通知相關的Job去執行。分離的好處是:

1.你可以為某個Job關聯多個Trigger,其中任何一個條件滿足都可以觸發job執行,這樣可以完成一些組合的高級觸發條件

2.Trigger失效后(比如:一個永遠都不能滿足的條件),你不必去聲明一個新的job,代替的是你可以為job關聯一個新的Triggerjob可以繼續執行。

目前的Quartz實現中,存在兩種TriggerSimpleTriggerCronTrigger,在spring中分別用SimpleTriggerBeanCronTriggerBean對其進行封裝。SimpleTrigger是簡單觸發器,如從某日到某日,每個一定時間進行一次提醒,在這段時間內進行多少次提醒;CronTrigger是復雜觸發器,用來執行calendar-like的任務,可設定一些復雜的觸發規則,如每年的x月的第y個星期五,或是每個星期天的幾點進行提醒。后面附加一個日常語義與cronTrigger的轉化

 

Trigger

private String name;//名稱

private String group = Scheduler.DEFAULT_GROUP;//

private String jobName;//所關聯的jobDetail的名稱

private String jobGroup = Scheduler.DEFAULT_GROUP;//所關聯的jobDetail的組

private String description;//描述

private JobDataMap jobDataMap;//該觸發器存儲的數據,在回調的時候也可以使用

private boolean volatility = false;//是否持久化到org.quartz.spi.JobStore

 

SimpleTrigger

private Date startTime = null;//開始日期

private Date endTime = null;//結束日期

private Date nextFireTime = null;//下次的觸發時間

private Date previousFireTime = null;//上次的觸發時間

private int repeatCount = 0;//重復次數

private long repeatInterval = 0;//重復間隔

private int timesTriggered = 0;/已觸發的次數

 

SimpleTriggerBean

private JobDetail jobDetail;//所關聯的JobDetail,方便在配置文件中使用

 

CornTrigger

private CronExpression cronEx = null;//觸發條件表達式,它有一個String型的setter

private Date startTime = null;//開始日期

private Date endTime = null;//結束日期

private Date nextFireTime = null;//下次的觸發時間

private Date previousFireTime = null;//上次的觸發時間

private transient TimeZone timeZone = null;//所在時區

 

CronTriggerBean

private JobDetail jobDetail;//所關聯的JobDetail,方便在配置文件中使用

 

Scheduler的常用方法

添加一個定時任務:

Date scheduleJob(JobDetail jobDetail,Trigger trigger)

 

修改一個定時任務,主要是更改trigger

Date rescheduleJob(String triggerName, String groupName, Trigger newTrigger)

 

刪除一個定時任務,同時也會將于該jobDetail關聯的trigger一并刪除:

boolean deleteJob(String jobName,String jobGroup)

 

取得所有的jobDetail

String[] getJobGroupNames() 

 

取得某個group下的所有的jobDetail

String[] getJobNames(String groupName)

 

取得指定的jobDetail

JobDetail getJobDetail(String jobName, String jobGroup)

 

取得指定的jobDetail的所有的Trigger

Trigger[] getTriggersOfJob(String jobName, String groupName)

 

取得指定的Trigger

Trigger getTrigger(String triggerName, String triggerGroup)

 

Quartz的存儲:

Quartz默認的是使用RAM存儲所有的信息,但是這樣的話,當我們重啟服務器后,之前的所有的定時任務就全消失了。為了讓服務器重啟以后,我們的定時任務仍不丟失,我們可采用數據庫持久化定時任務。

首先要先建立數據庫,在quartz-1.6.0\docs\dbTables下,選擇自己使用的數據庫的sql腳本,建立相應的數據庫表。

WEB-INF下加一個quartz.properties。我們可以在 quartz-1.6.0\examples\example10 中找到該文件的樣例

 

 

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX #表明使用JDBC進行持久化

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

org.quartz.jobStore.useProperties = false

org.quartz.jobStore.dataSource = myDS

org.quartz.jobStore.tablePrefix = QRTZ_     #該值盡量不要改動,如改動,還要相應的修改sql腳本

org.quartz.jobStore.isClustered = false

 

org.quartz.dataSource.myDS.driver = net.sourceforge.jtds.jdbc.Driver

org.quartz.dataSource.myDS.URL = jdbc:jtds:sqlserver://192.168.1.101:1433/Northwind;autoReconnect=true

org.quartz.dataSource.myDS.user = sa

org.quartz.dataSource.myDS.password =

org.quartz.dataSource.myDS.maxConnections = 5

 

 

日常語義與cronTrigger的轉化,以下setter,getter省略

 

java 代碼

1.public class TDateRange{   

2. private int startType = 2;//開始類型。默認的使用2表示使用開始日期   

3. private Date startDate = new Date();//開始日期    

4. private int endType = 0;//結束類型。0表示無結束時間;1表示重復n次后結束;2表示使用結束日期   

5. private Date endDate = new Date();//結束日期   

6. private int occurrences;//執行次數   

7.}   

8.public class TFrequency{   

9. //0:無重復提醒   

10. //1:每every   

11. //2:每個工作日detail=1,2,3,4,5   

12. //3:每every周后的星期detail   

13. //4:每every月的detail   

14. //5:每every月的第num1個星期num2   

15. //6:每年num1num2   

16. //7:每年every月的第num1個月的星期num2   

17. private int type = 0;//頻率類型   

18. private int every = 0;   

19. private String detail = "";   

20. private String num1 = "";   

21. private String num2 = "";   

22.}   

23.  

24.private String formatQuartzString(){   

25. String quartzStr = "";   

26. tiggernote="";   

27. //     

28. quartzStr = "0 "+this.dateRange.getStartDate().getMinutes()+" "+this.dateRange.getStartDate().getHours()+" ";   

29. switch(this.frequency.getType()){   

30. case 0://無重復提醒   

31. quartzStr += this.dateRange.getStartDate().getDate()+" "+(this.dateRange.getStartDate().getMonth()+1)+" ? "+(this.dateRange.getStartDate().getYear()+1900);   

32. tiggernote+="起始時間:"+quartzStr;   

33. break;   

34. case 1://XX天提醒   

35. quartzStr += "*/"+this.frequency.getEvery()+" * ? ";   

36. tiggernote+=""+this.frequency.getEvery()+"提醒";   

37. break;   

38. case 2://每個工作日detail=1,2,3,4,5   

39. //quartzStr += "? * 2-6";   

40. quartzStr ="0 */1 * * * ?"; //測試   

41. tiggernote+="每個工作日1,2,3,4,5提醒";   

42. break;   

43. case 3://every周后的星期detail   

44. quartzStr += "? * "+this.frequency.getDetail()+"/"+this.frequency.getEvery();   

45. tiggernote+=""+this.frequency.getEvery()+"周星期"+this.frequency.getDetail()+"";   

46. break;   

47. case 4://every個月的detail   

48. quartzStr += this.frequency.getDetail()+" */"+this.frequency.getEvery()+" ?";   

49. tiggernote+=""+this.frequency.getEvery()+""+this.frequency.getDetail()+"";   

50. break;   

51. case 5://every個月的第num1個星期num2   

52. quartzStr += "? */"+this.frequency.getEvery()+" "+this.frequency.getNum2();   

53. //星期   

54. if(Integer.valueOf(this.frequency.getNum1()).intValue()>0){   

55.  quartzStr += "#"+this.frequency.getNum1();   

56.  tiggernote+=""+this.frequency.getEvery()+"月第"+this.frequency.getNum1()+"個星期"+this.frequency.getNum2()+"";   

57. }else{   

58.  quartzStr += "L";   

59.  tiggernote+=""+this.frequency.getEvery()+"月星期"+this.frequency.getNum2();   

60. }   

61. break;   

62. case 6://每年num1num2   

63. quartzStr += this.frequency.getNum2()+" "+this.frequency.getNum1()+" ?";   

64.  tiggernote+="每年"+this.frequency.getNum1()+""+this.frequency.getNum2()+"";   

65. break;   

66. case 7://每年every月的第num1個星期num2   

67. quartzStr += "? "+this.getFrequency().getEvery()+" "+this.getFrequency().getNum2();   

68. //星期   

69. if(Integer.valueOf(this.frequency.getNum1()).intValue()>0){   

70.  quartzStr += "#"+this.frequency.getNum1();   

71.  tiggernote+="每年"+this.getFrequency().getEvery()+"月的第"+this.frequency.getNum1()+"個星期"+this.getFrequency().getNum2()+"";   

72. }else{    

73.  quartzStr += "L";   

74.  tiggernote+="每年"+this.getFrequency().getEvery()+"月的"+this.getFrequency().getNum2()+"";   

75. }    

76. break;   

77. default :   

78. }   

79. log.debug("quartzStr="+quartzStr);   

80. return quartzStr;   

81.}   

Scheduler

Scheduler 是一個計劃集,其中可以包含多個 JobDetail Trigger 組成的計劃任務。

Quartz中,我們可以通過

SchedulerFactory scheduleFactory = new StdSchedulerFactory();

Scheduler scheduler = scheduleFactory.getScheduler();

來取得scheduler,通過調用scheduler.start()來啟動quartz

spring中,org.springframework.scheduling.quartz.SchedulerFactoryBean是對Quartzorg.quartz.Scheduler的封裝,通過上面的配置,在spring啟動的時候,quartz就會跟隨著啟動,不需要再用scheduler.start()來啟動。在spring中,如果要取得scheduler,可通過上面的配置文件那樣,將SchedulerFactoryBean注入到schdeuler中。

 

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/andilyliao/archive/2007/12/07/1922153.aspx

 

官方地址:http://www.quartz-scheduler.org/

 本文由用戶 jack_gogo 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!