【Quartz】Quartz存儲與持久化-基於quartz.properties的配置

林炳文Evankaka原創作品。轉載請註明出處http://blog.csdn.net/evankaka

一、   Quartz存儲與持久化

         Quartz提供兩種基本作業存儲類型。第一種類型叫做RAMJobStore,第二種類型叫做JDBC作業存儲。在默認情況下Quartz將任務調度的運行信息保存在內存中,這種方法提供了最佳的性能,因爲內存中數據訪問最快。不足之處是缺乏數據的持久性,當程序路途停止或系統崩潰時,所有運行的信息都會丟失。

         比如我們希望安排一個執行100次的任務,如果執行到50次時系統崩潰了,系統重啓時任務的執行計數器將從0開始。在大多數實際的應用中,我們往往並不需要保存任務調度的現場數據,因爲很少需要規劃一個指定執行次數的任務。對於僅執行一次的任務來說,其執行條件信息本身應該是已經持久化的業務數據(如鎖定到期解鎖任務,解鎖的時間應該是業務數據),當執行完成後,條件信息也會相應改變。當然調度現場信息不僅僅是記錄運行次數,還包括調度規則、JobDataMap中的數據等等。

        如果確實需要持久化任務調度信息,Quartz允許你通過調整其屬性文件,將這些信息保存到數據庫中。使用數據庫保存任務調度信息後,即使系統崩潰後重新啓動,任務的調度信息將得到恢復。如前面所說的例子,執行50次崩潰後重新運行,計數器將從51開始計數。使用了數據庫保存信息的任務稱爲持久化任務。

對比

類型

優點

缺點

RAMJobStore

不要外部數據庫,配置容易,運行速度快

因爲調度程序信息是存儲在被分配給JVM的內存裏面,所以,當應用程序停止運行時,所有調度信息將被丟失。另外因爲存儲到JVM內存裏面,所以可以存儲多少個JobTrigger將會受到限制

JDBC作業存儲

支持集羣,因爲所有的任務信息都會保存到數據庫中,可以控制事物,還有就是如果應用服務器關閉或者重啓,任務信息都不會丟失,並且可以恢復因服務器關閉或者重啓而導致執行失敗的任務

運行速度的快慢取決與連接數據庫的快慢


二、Quartz存儲實例

本文工程免費下載

下面開始說實現的步驟吧:


2.1、建立數據存儲表

        因爲需要把quartz的數據保存到數據庫,所以要建立相關的數據庫。這個可以從下載到的quartz包裏面找到對應的sql腳本,目前可以支持MySQL,DB2,Oracle等主流的數據庫,自己可以根據項目需要選擇合適的腳本運行。

        我的項目是mysql的,就在數據中建立了一個quartz的database,然後執行tables_mysql_innodb.sql腳本建表。其中腳本 文件位於:E:\JarCom\quartz-2.2.1\docs\dbTables(根據你Quartz放置的目錄會不同)

然後打開MySql的終端:

先建立一個數據庫,名爲quartz;

然後執行腳本:

運行結果:

首先會出現如下錯誤:

這裏有四張數據表創建失敗。

解決方法:

將tables_mysql_innodb.sql中的TYENGINEPE=InnoDB全部都替換成ENGINE=InnoDB;

再次執行:


表示數據庫表創建成功了


表建立好後可以看到相關的table

+————————–+
| Tables_in_quartz         |
+————————–+
| QRTZ_BLOB_TRIGGERS       |
| QRTZ_CALENDARS           |
| QRTZ_CRON_TRIGGERS       |
| QRTZ_FIRED_TRIGGERS      |
| QRTZ_JOB_DETAILS         |
| QRTZ_LOCKS               |
| QRTZ_PAUSED_TRIGGER_GRPS |
| QRTZ_SCHEDULER_STATE     |
| QRTZ_SIMPLE_TRIGGERS     |
| QRTZ_SIMPROP_TRIGGERS    |
| QRTZ_TRIGGERS            |
+————————–+

2.2、新建工程並導入包

新建一個Java工程,導入相關的jar包

這裏我就不多說了,可以到官網上去下載,本文使用的是最新的2,2.1

quartz:http://www.quartz-scheduler.org/

及mySql操作的包,和一個commons-lang包,整個工作最終目錄如下:



然後,需要在項目中加上對應的配置。

2.3、首先是quartz.properties的配置

[html] view plain copy
  1. # Default Properties file for use by StdSchedulerFactory  
  2. # to create a Quartz Scheduler Instance, if a different  
  3. # properties file is not explicitly specified.  
  4. #  
  5.    
  6. #集羣配置  
  7. org.quartz.scheduler.instanceName: DefaultQuartzScheduler  
  8. org.quartz.scheduler.rmi.export: false  
  9. org.quartz.scheduler.rmi.proxy: false  
  10. org.quartz.scheduler.wrapJobExecutionInUserTransaction: false  
  11.    
  12. org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool  
  13. org.quartz.threadPool.threadCount: 10  
  14. org.quartz.threadPool.threadPriority: 5  
  15. org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true  
  16.    
  17. org.quartz.jobStore.misfireThreshold: 60000  
  18.    
  19. #============================================================================  
  20. # Configure JobStore  
  21. #============================================================================  
  22.    
  23. #默認配置,數據保存到內存  
  24. #org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore  
  25. #持久化配置  
  26. org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX  
  27. org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate  
  28. org.quartz.jobStore.useProperties:true  
  29. #數據庫表前綴  
  30. #org.quartz.jobStore.tablePrefix:qrtz_  
  31. #org.quartz.jobStore.dataSource:qzDS  
  32.    
  33. #============================================================================  
  34. # Configure Datasources  
  35. #============================================================================  
  36. #JDBC驅動  
  37. #org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver  
  38. #org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartz  
  39. #org.quartz.dataSource.qzDS.user:root  
  40. #org.quartz.dataSource.qzDS.password:christmas258@  
  41. #org.quartz.dataSource.qzDS.maxConnection:10  

其中org.quartz.jobStore.class是指明quartz的持久化用數據庫來保存,

而org.quartz.jobStore.driverDelegateClass是根據選擇的數據庫類型不同而不同,我這裏的是mysql,所以是org.quartz.impl.jdbcjobstore.StdJDBCDelegate。

Quartz的屬性配置文件主要包括三方面的信息:

1)集羣信息;

2)調度器線程池;

3)任務調度現場數據的保存。

·調度器屬性

第一部分有兩行,分別設置調度器的實例名(instanceName) 和實例 ID (instanceId)。屬性 org.quartz.scheduler.instanceName 可以是你喜歡的任何字符串。它用來在用到多個調度器區分特定的調度器實例。多個調度器通常用在集羣環境中。現在的話,設置如下的一個字符串就行:org.quartz.scheduler.instanceName :DefaultQuartzScheduler實際上,這也是當你沒有該屬性配置時的默認值。

調度器的第二個屬性是 org.quartz.scheduler.instanceId。和 instaneName 屬性一樣,instanceId 屬性也允許任何字符串。這個值必須是在所有調度器實例中是唯一的,尤其是在一個集羣當中。假如你想 Quartz 幫你生成這個值的話,可以設置爲 AUTO。如果 Quartz 框架是運行在非集羣環境中,那麼自動產生的值將會是 NON_CLUSTERED。假如是在集羣環境下使用 Quartz,這個值將會是主機名加上當前的日期和時間。大多情況下,設置爲 AUTO 即可。
·線程池屬性

接下來的部分是設置有關線程必要的屬性值,這些線程在 Quartz 中是運行在後臺擔當重任的。threadCount 屬性控制了多少個工作者線程被創建用來處理 Job。原則上是,要處理的 Job 越多,那麼需要的工作者線程也就越多。threadCount 的數值至少爲 1。Quartz 沒有限定你設置工作者線程的最大值,但是在多數機器上設置該值超過100的話就會顯得相當不實用了,特別是在你的 Job 執行時間較長的情況下。這項沒有默認值,所以你必須爲這個屬性設定一個值。

threadPriority 屬性設置工作者線程的優先級。優先級別高的線程比級別低的線程更優先得到執行。threadPriority 屬性的最大值是常量 java.lang.Thread.MAX_PRIORITY,等於10。最小值爲常量 java.lang.Thread.MIN_PRIORITY,爲1。這個屬性的正常值是 Thread.NORM_PRIORITY,爲5。大多情況下,把它設置爲5,這也是沒指定該屬性的默認值。

最後一個要設置的線程池屬性是 org.quartz.threadPool.class。這個值是一個實現了 org.quartz.spi.ThreadPool 接口的類的全限名稱。Quartz 自帶的線程池實現類是 org.quartz.smpl.SimpleThreadPool,它能夠滿足大多數用戶的需求。這個線程池實現具備簡單的行爲,並經很好的測試過。它在調度器的生命週期中提供固定大小的線程池。你能根據需求創建自己的線程池實現,如果你想要一個隨需可伸縮的線程池時也許需要這麼做。這個屬性沒有默認值,你必須爲其指定值。

·作業存儲設置

作業存儲部分的設置描述了在調度器實例的生命週期中,Job 和 Trigger 信息是如何被存儲的。我們還沒有談論到作業存儲和它的目的;因爲對當前例子是非必的,所以我們留待以後說明。現在的話,你所要了解的就是我們存儲調度器信息在內存中而不是在關係型數據庫中就行了。

把調度器信息存儲在內存中非常的快也易於配置。當調度器進程一旦被終止,所有的 Job 和 Trigger 的狀態就丟失了。要使 Job 存儲在內存中需通過設置  org.quartz.jobStrore.class 屬性爲 org.quartz.simpl.RAMJobStore。假如我們不希望在 JVM 退出之後丟失調度器的狀態信息的話,我們可以使用關係型數據庫來存儲這些信息。這需要另一個作業存儲(JobStore) 實現,我們在後面將會討論到。第五章“Cron Trigger 和其他”和第六章“作業存儲和持久化”會提到你需要用到的不同類型的作業存儲實現。

2.4、創建Job類

[java] view plain copy
  1. package com.mucfc;  
  2.   
  3. import java.text.SimpleDateFormat;  
  4. import java.util.Date;  
  5.   
  6. import org.apache.log4j.Logger;  
  7. import org.quartz.Job;  
  8. import org.quartz.JobExecutionContext;  
  9. import org.quartz.JobExecutionException;  
  10.   
  11. public class MyJob implements Job{  
  12.     private static final Logger logger = Logger.getLogger(MyJob.class);    
  13.     @Override  
  14.     public void execute(JobExecutionContext context)  
  15.             throws JobExecutionException {  
  16.         System.out.println("Hello quzrtz  "+  
  17.                 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ").format(new Date()));  
  18.           
  19.     }  
  20.   
  21. }  

2.5、創建Job的執行類:

[java] view plain copy
  1. package com.mucfc;  
  2.   
  3. import java.text.ParseException;  
  4. import java.util.List;  
  5.   
  6. import org.apache.commons.lang.StringUtils;  
  7. import org.quartz.CronScheduleBuilder;  
  8. import org.quartz.Job;  
  9. import org.quartz.JobBuilder;  
  10. import org.quartz.JobDetail;  
  11. import org.quartz.JobKey;  
  12. import org.quartz.Scheduler;  
  13. import org.quartz.SchedulerException;  
  14. import org.quartz.SchedulerFactory;  
  15. import org.quartz.SimpleScheduleBuilder;  
  16. import org.quartz.SimpleTrigger;  
  17. import org.quartz.Trigger;  
  18. import org.quartz.TriggerBuilder;  
  19. import org.quartz.TriggerKey;  
  20. import org.quartz.impl.StdSchedulerFactory;  
  21. import org.springframework.beans.factory.annotation.Autowired;  
  22. import org.springframework.context.ApplicationContext;  
  23. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  24.   
  25. public class QuartzTest {  
  26.   
  27.     private static SchedulerFactory sf = new StdSchedulerFactory();  
  28.     private static String JOB_GROUP_NAME = "ddlib";  
  29.     private static String TRIGGER_GROUP_NAME = "ddlibTrigger";  
  30.     public static void main(String[] args) throws SchedulerException,  
  31.             ParseException {  
  32.         startSchedule();  
  33.         //resumeJob();  
  34.     }  
  35.     /** 
  36.      * 開始一個simpleSchedule()調度 
  37.      */  
  38.     public static void startSchedule() {  
  39.         try {  
  40.             // 1、創建一個JobDetail實例,指定Quartz  
  41.             JobDetail jobDetail = JobBuilder.newJob(MyJob.class)  
  42.             // 任務執行類  
  43.                     .withIdentity("job1_1""jGroup1")  
  44.                     // 任務名,任務組  
  45.                     .build();  
  46.             // 2、創建Trigger  
  47.             SimpleScheduleBuilder builder = SimpleScheduleBuilder  
  48.                     .simpleSchedule()  
  49.                     // 設置執行次數  
  50.                     .repeatSecondlyForTotalCount(100);  
  51.             Trigger trigger = TriggerBuilder.newTrigger()  
  52.                     .withIdentity("trigger1_1""tGroup1").startNow()  
  53.                     .withSchedule(builder).build();  
  54.             // 3、創建Scheduler  
  55.             Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();  
  56.             scheduler.start();  
  57.             // 4、調度執行  
  58.             scheduler.scheduleJob(jobDetail, trigger);  
  59.             try {  
  60.                 Thread.sleep(60000);  
  61.             } catch (InterruptedException e) {  
  62.                 e.printStackTrace();  
  63.             }  
  64.   
  65.             scheduler.shutdown();  
  66.   
  67.         } catch (SchedulerException e) {  
  68.             e.printStackTrace();  
  69.         }  
  70.     }  
  71.   
  72.     /** 
  73.      * 從數據庫中找到已經存在的job,並重新開戶調度 
  74.      */  
  75.     public static void resumeJob() {  
  76.         try {  
  77.   
  78.             SchedulerFactory schedulerFactory = new StdSchedulerFactory();  
  79.             Scheduler scheduler = schedulerFactory.getScheduler();  
  80.             // ①獲取調度器中所有的觸發器組  
  81.             List<String> triggerGroups = scheduler.getTriggerGroupNames();  
  82.             // ②重新恢復在tgroup1組中,名爲trigger1_1觸發器的運行  
  83.             for (int i = 0; i < triggerGroups.size(); i++) {  
  84.                 List<String> triggers = scheduler.getTriggerGroupNames();  
  85.                 for (int j = 0; j < triggers.size(); j++) {  
  86.                     Trigger tg = scheduler.getTrigger(new TriggerKey(triggers  
  87.                             .get(j), triggerGroups.get(i)));  
  88.                     // ②-1:根據名稱判斷  
  89.                     if (tg instanceof SimpleTrigger  
  90.                             && tg.getDescription().equals("tgroup1.trigger1_1")) {  
  91.                         // ②-1:恢復運行  
  92.                         scheduler.resumeJob(new JobKey(triggers.get(j),  
  93.                                 triggerGroups.get(i)));  
  94.                     }  
  95.                 }  
  96.   
  97.             }  
  98.             scheduler.start();  
  99.         } catch (Exception e) {  
  100.             e.printStackTrace();  
  101.   
  102.         }  
  103.     }  
  104. }  

2.6、執行:

然後打開數據庫,輸入

[sql] view plain copy
  1. use quartz;  
  2. selectfrom QRTZ_SIMPLE_TRIGGERS  

然後把上面的程序停了,點擊右上角的紅框框

把主代碼改成:

[java] view plain copy
  1. public static void main(String[] args) throws SchedulerException,  
  2.             ParseException {  
  3.         //startSchedule();  
  4.         resumeJob();  
  5.     }  

輸出結果:


同時,查找數據中的表:

發現有變化 了。。TRIGGER_GROUP表示的執行的次數,TRIGGER_GROUP表示被激發的次數

來回的不斷停止程序再重新開始程序,結果如下。

重新停止再打開後,TRIGGER_GROUP變爲39,他會減去上一次運行中TRIGGER_GROUP的次數,TRIGGER_GROUP每次運行都會從0開始計算


然後等100次執行完後,這個調度就會從數據表中刪除:

結果如下:


這時,該表中的記錄已經變空。

值得注意的是,如果你使用JDBC保存任務調度數據時,當你運行代碼

[java] view plain copy
  1. startSchedule();  
  2. //resumeJob();  
然後退出,當再次希望運行

[java] view plain copy
  1. startSchedule();  
  2. //resumeJob();  

時,系統將拋出JobDetail重名的異常:Unable to store Job with name: 'job1_1' and group: 'jGroup1', because one already exists with this identification.因爲每次調用Scheduler#scheduleJob()時,Quartz都會將JobDetailTrigger的信息保存到數據庫中,如果數據表中已經同名的JobDetailTrigger,異常就產生了。

這裏再次運行應該這樣做:

[java] view plain copy
  1. //startSchedule();  
  2. resumeJob();  


三、一些說明

一些相關的意義:

[sql] view plain copy
  1. QRTZ_CALENDARS 以 Blob 類型存儲 Quartz 的 Calendar 信息   
  2. QRTZ_CRON_TRIGGERS 存儲 Cron Trigger,包括Cron表達式和時區信息   
  3. QRTZ_FIRED_TRIGGERS 存儲與已觸發的 Trigger 相關的狀態信息,以及相聯 Job的執行信息QRTZ_PAUSED_TRIGGER_GRPS 存儲已暫停的 Trigger組的信息   
  4. QRTZ_SCHEDULER_STATE 存儲少量的有關 Scheduler 的狀態信息,和別的Scheduler實例(假如是用於一個集羣中)   
  5. QRTZ_LOCKS 存儲程序的悲觀鎖的信息(假如使用了悲觀鎖)   
  6. QRTZ_JOB_DETAILS 存儲每一個已配置的 Job 的詳細信息   
  7. QRTZ_JOB_LISTENERS 存儲有關已配置的 JobListener的信息   
  8. QRTZ_SIMPLE_TRIGGERS存儲簡單的Trigger,包括重複次數,間隔,以及已觸的次數   
  9. QRTZ_BLOG_TRIGGERS Trigger 作爲 Blob 類型存儲(用於 Quartz 用戶用JDBC創建他們自己定製的 Trigger 類型,JobStore並不知道如何存儲實例的時候)   
  10. QRTZ_TRIGGER_LISTENERS 存儲已配置的 TriggerListener的信息   
  11. QRTZ_TRIGGERS 存儲已配置的 Trigger 的信息   

--------------------------------------------------------------------------------------------------

quartz 持久化數據庫表格字段解釋
建表,SQL語句在dbTables文件夾中可以找到,介紹下主要的幾張表: 
[sql] view plain copy
  1. 表qrtz_job_details:保存job詳細信息,該表需要用戶根據實際情況初始化   
  2. job_name:集羣中job的名字,該名字用戶自己可以隨意定製,無強行要求   
  3. job_group:集羣中job的所屬組的名字,該名字用戶自己隨意定製,無強行要求   
  4. job_class_name:集羣中個notejob實現類的完全包名,quartz就是根據這個路徑到classpath找到該job類   
  5. is_durable:是否持久化,把該屬性設置爲1,quartz會把job持久化到數據庫中   
  6. job_data:一個blob字段,存放持久化job對象   
  7.   
  8. 表qrtz_triggers: 保存trigger信息   
  9. trigger_name:trigger的名字,該名字用戶自己可以隨意定製,無強行要求   
  10. trigger_group:trigger所屬組的名字,該名字用戶自己隨意定製,無強行要求   
  11. job_name:qrtz_job_details表job_name的外鍵   
  12. job_group:qrtz_job_details表job_group的外鍵   
  13. trigger_state:當前trigger狀態,設置爲ACQUIRED,如果設置爲WAITING,則job不會觸發   
  14. trigger_cron:觸發器類型,使用cron表達式   
  15.   
  16. 表qrtz_cron_triggers:存儲cron表達式表   
  17. trigger_name:qrtz_triggers表trigger_name的外鍵   
  18. trigger_group:qrtz_triggers表trigger_group的外鍵   
  19. cron_expression:cron表達式   
  20.    
  21. 表qrtz_scheduler_state:存儲集羣中note實例信息,quartz會定時讀取該表的信息判斷集羣中每個實例的當前狀態   
  22. instance_name:之前配置文件中org.quartz.scheduler.instanceId配置的名字,就會寫入該字段,如果設置爲AUTO,quartz會根據物理機名和當前時間產生一個名字   
  23. last_checkin_time:上次檢查時間   
  24. checkin_interval:檢查間隔時間   


配置quartz.properties文件:
[html] view plain copy
  1. #調度標識名 集羣中每一個實例都必須使用相同的名稱 org.quartz.scheduler.instanceName:scheduler  
  2. #ID設置爲自動獲取 每一個必須不同 org.quartz.scheduler.instanceId :AUTO  
  3. #數據保存方式爲持久化 org.quartz.jobStore.class :org.quartz.impl.jdbcjobstore.JobStoreTX  
  4. #數據庫平臺 org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate#數據庫別名 隨便取org.quartz.jobStore.dataSource : myXADS  
  5. #表的前綴 org.quartz.jobStore.tablePrefix : QRTZ_  
  6. #設置爲TRUE不會出現序列化非字符串類到 BLOB 時產生的類版本問題org.quartz.jobStore.useProperties : true  
  7. #加入集羣 org.quartz.jobStore.isClustered : true  
  8. #調度實例失效的檢查時間間隔 org.quartz.jobStore.clusterCheckinInterval:20000   
  9. #容許的最大作業延長時間 org.quartz.jobStore.misfireThreshold :60000  
  10. #ThreadPool 實現的類名 org.quartz.threadPool.class:org.quartz.simpl.SimpleThreadPool  
  11. #線程數量 org.quartz.threadPool.threadCount : 10  
  12. #線程優先級 org.quartz.threadPool.threadPriority : 5  
  13. #自創建父線程org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true   
  14. #設置數據源org.quartz.dataSource.myXADS.jndiURL: CT  
  15. #jbdi類名 org.quartz.dataSource.myXADS.java.naming.factory.initial :weblogic.jndi.WLInitialContextFactory#URLorg.quartz.dataSource.myXADS.java.naming.provider.url:=t3://localhost:7001  
  16. 【注】:在J2EE工程中如果想用數據庫管理Quartz的相關信息,就一定要配置數據源,這是Quartz的要求。  

林炳文Evankaka原創作品。轉載請註明出處http://blog.csdn.net/evankaka

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