Quartz.NET是一個開源的作業調度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#寫成,可用於winform和asp.net應用中。它提供了巨大的靈活性而不犧牲簡單性。你能夠用它來為執行一個作業而創建簡單的或復雜的調度。它有很多特征,如:數據庫支持,集群,插件,支持cron-like表達式等等。
你曾經需要應用執行一個任務嗎?這個任務每天或每周星期二晚上11:30,或許僅僅每個月的最后一天執行。一個自動執行而無須干預的任務在執行過程中如果發生一個嚴重錯誤,應用能夠知到其執行失敗並嘗試重新執行嗎?你和你的團隊是用.NET編程嗎?如果這些問題中任何一個你回答是,那么你應該使用Quartz.Net調度器。 Quartz.NET允許開發人員根據時間間隔(或天)來調度作業。它實現了作業和觸發器的多對多關系,還能把多個作業與不同的觸發器關聯。整合了 Quartz.NET的應用程序可以重用來自不同事件的作業,還可以為一個事件組合多個作業.
Quartz.NET入門
要開始使用 Quartz.NET,需要用 Quartz.NET API 對項目進行配置。步驟如下:
1. 到http://quartznet.sourceforge.net/download.html下載 Quartz.NET API,最新版本是0.6
2. 解壓縮Quartz.NET-0.6.zip 到目錄,根據你的項目情況用Visual Studio 2003或者Visual Studio 2005打開相應工程,編譯。你可以將它放進自己的應用中。Quartz.NET框架只需要少數的第三方庫,並且這些三方庫是必需的,你很可能已經在使用這些庫了。
3. 在Quartz.NET有一個叫做quartz.properties的配置文件,它允許你修改框架運行時環境。缺省是使用Quartz.dll里面的quartz.properties文件。當然你可以在應用程序配置文件中做相應的配置,下面是一個配置文件示例:
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <configSections>
- <section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
- </configSections>
- <quartz>
- <add key="quartz.scheduler.instanceName" value="ExampleDefaultQuartzScheduler" />
- <add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz" />
- <add key="quartz.threadPool.threadCount" value="10" />
- <add key="quartz.threadPool.threadPriority" value="2" />
- <add key="quartz.jobStore.misfireThreshold" value="60000" />
- <add key="quartz.jobStore.type" value="Quartz.Simpl.RAMJobStore, Quartz" />
- </quartz>
- </configuration>
為了方便讀者,我們使用Quartz.NET的例子代碼來解釋,現在來看一下 Quartz API 的主要組件。
調度器和作業
Quartz.NET框架的核心是調度器。調度器負責管理Quartz.NET應用運行時環境。調度器不是靠自己做所有的工作,而是依賴框架內一些非常重要的部件。Quartz不僅僅是線程和線程管理。為確保可伸縮性,Quartz.NET采用了基於多線程的架構。 啟動時,框架初始化一套worker線程,這套線程被調度器用來執行預定的作業。這就是Quartz.NET怎樣能並發運行多個作業的原理。Quartz.NET依賴一套松耦合的線程池管理部件來管理線程環境。作業是一個執行任務的簡單.NET類。任務可以是任何C#/VB.NET代碼。只需你實現Quartz.IJob接口並且在出現嚴重錯誤情況下拋出JobExecutionException異常即可。
IJob接口包含唯一的一個方法Execute(),作業從這里開始執行。一旦實現了IJob接口和Execute ()方法,當Quartz.NET確定該是作業運行的時候,它將調用你的作業。Execute()方法內就完全是你要做的事情。
通過實現 Quartz.IJob接口,可以使 .NET 類變成可執行的。清單 1 提供了 Quartz.IJob作業的一個示例。這個類用一條非常簡單的輸出語句覆蓋了 Execute(JobExecutionContext context) 方法。這個方法可以包含我們想要執行的任何代碼(所有的代碼示例都基於 Quartz.NET 0.6 ,它是編寫這篇文章時的穩定發行版)。
清單 1:作業
- using System;
- using System.Collections.Generic;
- using System.Text;
- using Common.Logging;
- using Quartz;
- namespace QuartzBeginnerExample
- {
- public class SimpleQuartzJob : IJob
- {
- private static ILog _log = LogManager.GetLogger(typeof(SimpleQuartzJob));
- /// <summary>
- /// Called by the <see cref="IScheduler" /> when a
- /// <see cref="Trigger" /> fires that is associated with
- /// the <see cref="IJob" />.
- /// </summary>
- public virtual void Execute(JobExecutionContext context)
- {
- try
- {
- // This job simply prints out its job name and the
- // date and time that it is running
- string jobName = context.JobDetail.FullName;
- _log.Info("Executing job: " + jobName + " executing at " + DateTime.Now.ToString("r"));
- }
- catch (Exception e)
- {
- _log.Info("--- Error in job!");
- JobExecutionException e2 = new JobExecutionException(e);
- // this job will refire immediately
- e2.RefireImmediately = true;
- throw e2;
- }
- }
- }
- }
請注意,Execute 方法接受一個 JobExecutionContext 對象作為參數。這個對象提供了作業實例的運行時上下文。特別地,它提供了對調度器和觸發器的訪問,這兩者協作來啟動作業以及作業的 JobDetail 對象的執行。Quartz.NET 通過把作業的狀態放在 JobDetail 對象中並讓 JobDetail 構造函數啟動一個作業的實例,分離了作業的執行和作業周圍的狀態。JobDetail 對象儲存作業的偵聽器、群組、數據映射、描述以及作業的其他屬性。
作業和觸發器:
Quartz.NET設計者做了一個設計選擇來從調度分離開作業。Quartz.NET中的觸發器用來告訴調度程序作業什么時候觸發。框架提供了一把觸發器類型,但兩個最常用的是SimpleTrigger和CronTrigger。SimpleTrigger為需要簡單打火調度而設計。
典型地,如果你需要在給定的時間和重復次數或者兩次打火之間等待的秒數打火一個作業,那么SimpleTrigger適合你。另一方面,如果你有許多復雜的作業調度,那么或許需要CronTrigger。
CronTrigger是基於Calendar-like調度的。當你需要在除星期六和星期天外的每天上午10點半執行作業時,那么應該使用CronTrigger。正如它的名字所暗示的那樣,CronTrigger是基於Unix克隆表達式的。
Cron表達式被用來配置CronTrigger實例。Cron表達式是一個由7個子表達式組成的字符串。每個子表達式都描述了一個單獨的日程細節。這些子表達式用空格分隔,分別表示:
1. Seconds 秒
2. Minutes 分鍾
3. Hours 小時
4. Day-of-Month 月中的天
5. Month 月
6. Day-of-Week 周中的天
7. Year (optional field) 年(可選的域)
一個cron表達式的例子字符串為"0 0 12 ? * WED",這表示“每周三的中午12:00”。
單個子表達式可以包含范圍或者列表。例如:前面例子中的周中的天這個域(這里是"WED")可以被替換為"MON-FRI", "MON, WED, FRI"或者甚至"MON-WED,SAT"。
通配符('*')可以被用來表示域中“每個”可能的值。因此在"Month"域中的*表示每個月,而在Day-Of-Week域中的*則表示“周中的每一天”。
所有的域中的值都有特定的合法范圍,這些值的合法范圍相當明顯,例如:秒和分域的合法值為0到59,小時的合法范圍是0到23,Day-of-Month中值得合法凡范圍是0到31,但是需要注意不同的月份中的天數不同。月份的合法值是0到11。或者用字符串JAN,FEB MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV 及DEC來表示。Days-of-Week可以用1到7來表示(1=星期日)或者用字符串SUN, MON, TUE, WED, THU, FRI 和SAT來表示.
'/'字符用來表示值的增量,例如, 如果分鍾域中放入'0/15',它表示“每隔15分鍾,從0開始”,如果在份中域中使用'3/20',則表示“小時中每隔20分鍾,從第3分鍾開始”或者另外相同的形式就是'3,23,43'。
'?'字符可以用在day-of-month及day-of-week域中,它用來表示“沒有指定值”。這對於需要指定一個或者兩個域的值而不需要對其他域進行設置來說相當有用。
'L'字符可以在day-of-month及day-of-week中使用,這個字符是"last"的簡寫,但是在兩個域中的意義不同。例如,在day-of-month域中的"L"表示這個月的最后一天,即,一月的31日,非閏年的二月的28日。如果它用在day-of-week中,則表示"7"或者"SAT"。但是如果在day-of-week域中,這個字符跟在別的值后面,則表示"當月的最后的周XXX"。例如:"6L" 或者 "FRIL"都表示本月的最后一個周五。當使用'L'選項時,最重要的是不要指定列表或者值范圍,否則會導致混亂。
'W' 字符用來指定距離給定日最接近的周幾(在day-of-week域中指定)。例如:如果你為day-of-month域指定為"15W",則表示“距離月中15號最近的周幾”。
'#'表示表示月中的第幾個周幾。例如:day-of-week域中的"6#3" 或者 "FRI#3"表示“月中第三個周五”。
作為一個例子,下面的Quartz.NET克隆表達式將在星期一到星期五的每天上午10點15分執行一個作業。
0 15 10 ? * MON-FRI
下面的表達式
0 15 10 ? * 6L 2007-2010
將在2007年到2010年的每個月的最后一個星期五上午10點15分執行作業。你不可能用SimpleTrigger來做這些事情。你可以用兩者之中的任何一個,但哪個跟合適則取決於你的調度需要。
清單 2 中的 SimpleTrigger 展示了觸發器的基礎:
清單2 SimpleTriggerRunner.cs
- using System;
- using System.Collections.Generic;
- using System.Text;
- using Common.Logging;
- using Quartz;
- using Quartz.Impl;
- namespace QuartzBeginnerExample
- {
- public class SimpleTriggerRunner
- {
- public virtual void Run()
- {
- ILog log = LogManager.GetLogger(typeof(SimpleTriggerExample));
- log.Info("------- Initializing -------------------");
- // First we must get a reference to a scheduler
- ISchedulerFactory sf = new StdSchedulerFactory();
- IScheduler sched = sf.GetScheduler();
- log.Info("------- Initialization Complete --------");
- log.Info("------- Scheduling Jobs ----------------");
- // jobs can be scheduled before sched.start() has been called
- // get a "nice round" time a few seconds in the future...
- DateTime ts = TriggerUtils.GetNextGivenSecondDate(null, 15);
- // job1 will only fire once at date/time "ts"
- JobDetail job = new JobDetail("job1", "group1", typeof(SimpleJob));
- SimpleTrigger trigger = new SimpleTrigger("trigger1", "group1");
- // set its start up time
- trigger.StartTime = ts;
- // set the interval, how often the job should run (10 seconds here)
- trigger.RepeatInterval = 10000;
- // set the number of execution of this job, set to 10 times.
- // It will run 10 time and exhaust.
- trigger.RepeatCount = 100;
- // schedule it to run!
- DateTime ft = sched.ScheduleJob(job, trigger);
- log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds",
- job.FullName, ft.ToString("r"), trigger.RepeatCount, (trigger.RepeatInterval / 1000)));
- log.Info("------- Starting Scheduler ----------------");
- // All of the jobs have been added to the scheduler, but none of the jobs
- // will run until the scheduler has been started
- sched.Start();
- log.Info("------- Started Scheduler -----------------");
- log.Info("------- Waiting 30 seconds... --------------");
- try
- {
- // wait 30 seconds to show jobs
- Thread.Sleep(30 * 1000);
- // executing...
- }
- catch (ThreadInterruptedException)
- {
- }
- log.Info("------- Shutting Down ---------------------");
- sched.Shutdown(true);
- log.Info("------- Shutdown Complete -----------------");
- // display some stats about the schedule that just ran
- SchedulerMetaData metaData = sched.GetMetaData();
- log.Info(string.Format("Executed {0} jobs.", metaData.NumJobsExecuted));
- }
- }
- }
清單 2 開始時實例化一個 SchedulerFactory,獲得此調度器。就像前面討論過的,創建 JobDetail 對象時,它的構造函數要接受一個 Job 作為參數。顧名思義,SimpleTrigger 實例相當原始。在創建對象之后,設置幾個基本屬性以立即調度任務,然后每 10 秒重復一次,直到作業被執行 100 次。
還有其他許多方式可以操縱 SimpleTrigger。除了指定重復次數和重復間隔,還可以指定作業在特定日歷時間執行,只需給定執行的最長時間或者優先級(稍后討論)。執行的最長時間可以覆蓋指定的重復次數,從而確保作業的運行不會超過最長時間。
清單 3 顯示了 CronTrigger 的一個示例。請注意 SchedulerFactory、Scheduler 和 JobDetail 的實例化,與 SimpleTrigger 示例中的實例化是相同的。在這個示例中,只是修改了觸發器。這里指定的 cron 表達式(“0/5 * * * * ?”)安排任務每 5 秒執行一次。
清單3 CronTriggerRunner.cs
- using System;
- using System.Collections.Generic;
- using System.Text;
- using Common.Logging;
- using Quartz;
- using Quartz.Impl;
- using System.Threading;
- namespace QuartzBeginnerExample
- {
- public class CronTriggerRunner
- {
- public virtual void Run()
- {
- ILog log = LogManager.GetLogger(typeof(CronTriggerRunner));
- log.Info("------- Initializing -------------------");
- // First we must get a reference to a scheduler
- ISchedulerFactory sf = new StdSchedulerFactory();
- IScheduler sched = sf.GetScheduler();
- log.Info("------- Initialization Complete --------");
- log.Info("------- Scheduling Jobs ----------------");
- // jobs can be scheduled before sched.start() has been called
- // job 1 will run every 20 seconds
- JobDetail job = new JobDetail("job1", "group1", typeof(SimpleQuartzJob));
- CronTrigger trigger = new CronTrigger("trigger1", "group1", "job1", "group1");
- trigger.CronExpressionString = "0/20 * * * * ?";
- sched.AddJob(job, true);
- DateTime ft = sched.ScheduleJob(trigger);
- log.Info(string.Format("{0} has been scheduled to run at: {1} and repeat based on expression: {2}", job.FullName, ft.ToString("r"), trigger.CronExpressionString));
- log.Info("------- Starting Scheduler ----------------");
- // All of the jobs have been added to the scheduler, but none of the
- // jobs
- // will run until the scheduler has been started
- sched.Start();
- log.Info("------- Started Scheduler -----------------");
- log.Info("------- Waiting five minutes... ------------");
- try
- {
- // wait five minutes to show jobs
- Thread.Sleep(300 * 1000);
- // executing...
- }
- catch (ThreadInterruptedException)
- {
- }
- log.Info("------- Shutting Down ---------------------");
- sched.Shutdown(true);
- log.Info("------- Shutdown Complete -----------------");
- SchedulerMetaData metaData = sched.GetMetaData();
- log.Info(string.Format("Executed {0} jobs.", metaData.NumJobsExecuted));
- }
- }
- }
如上所示,只用作業和觸發器,就能訪問大量的功能。但是,Quartz 是個豐富而靈活的調度包,對於願意研究它的人來說,它還提供了更多功能。下一節討論 Quartz 的一些高級特性。
作業管理和存儲
作業一旦被調度,調度器需要記住並且跟蹤作業和它們的執行次數。如果你的作業是30分鍾后或每30秒調用,這不是很有用。事實上,作業執行需要非常准確和即時調用在被調度作業上的Execute()方法。Quartz通過一個稱之為作業存儲(JobStore)的概念來做作業存儲和管理。
有效作業存儲
Quartz提供兩種基本作業存儲類型。第一種類型叫做RAMJobStore,它利用通常的內存來持久化調度程序信息。這種作業存儲類型最容易配置、構造和運行。Quartz.net缺省使用的就是RAMJobStore。對許多應用來說,這種作業存儲已經足夠了。
然而,因為調度程序信息是存儲在被分配在內存里面,所以,當應用程序停止運行時,所有調度信息將被丟失。如果你需要在重新啟動之間持久化調度信息,則將需要第二種類型的作業存儲。為了修正這個問題,Quartz.NET 提供了 AdoJobStore。顧名思義,作業倉庫通過 ADO.NET把所有數據放在數據庫中。數據持久性的代價就是性能降低和復雜性的提高。它將所有的數據通過ADO.NET保存到數據庫可中。它的配置要比RAMJobStore稍微復雜,同時速度也沒有那么快。但是性能的缺陷不是非常差,尤其是如果你在數據庫表的主鍵上建立索引。
設置AdoJobStore
AdoJobStore幾乎可以在任何數據庫上工作,它廣泛地使用Oracle, MySQL, MS SQLServer2000, HSQLDB, PostreSQL 以及 DB2。要使用AdoJobStore,首先必須創建一套Quartz使用的數據庫表,可以在Quartz 的database/tables找到創建庫表的SQL腳本。如果沒有找到你的數據庫類型的腳本,那么找到一個已有的,修改成為你數據庫所需要的。需要注意的一件事情就是所有Quartz庫表名都以QRTZ_作為前綴(例如:表"QRTZ_TRIGGERS",及"QRTZ_JOB_DETAIL")。實際上,可以你可以將前綴設置為任何你想要的前綴,只要你告訴AdoJobStore那個前綴是什么即可(在你的Quartz屬性文件中配置)。對於一個數據庫中使用多個scheduler實例,那么配置不同的前綴可以創建多套庫表,十分有用。
一旦數據庫表已經創建,在配置和啟動AdoJobStore之前,就需要作出一個更加重要的決策。你要決定在你的應用中需要什么類型的事務。如果不想將scheduling命令綁到其他的事務上,那么你可以通過對JobStore使用JobStoreTX來讓Quartz幫你管理事務(這是最普遍的選擇)。
最后的疑問就是如何建立獲得數據庫聯接的數據源(DataSource)。Quartz屬性中定義數據源是通過提供所有聯接數據庫的信息,讓Quartz自己創建和管理數據源。
要使用AdoJobStore(假定使用StdSchedulerFactory),首先需要設置Quartz配置中的quartz.jobStore.type屬性為Quartz.Impl.AdoJobStore.JobStoreTX, Quartz。
配置 Quartz使用 JobStoreTx
quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz
下一步,需要為JobStore 選擇一個DriverDelegate , DriverDelegate負責做指定數據庫的所有ADO.NET工作。StdADO.NETDelegate是一個使用vanilla" ADO.NET代碼(以及SQL語句)來完成工作的代理。如果數據庫沒有其他指定的代理,那么就試用這個代理。只有當使用StdADO.NETDelegate發生問題時,我們才會使用數據庫特定的代理(這看起來非常樂觀。其他的代理可以在Quartz.Impl.AdoJobStor命名空間找到。)。其他的代理包括PostgreSQLDelegate ( 專為PostgreSQL 7.x)。
一旦選擇好了代理,就將它的名字設置給AdoJobStore。
配置AdoJobStore 使用DriverDelegate
quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz
接下來,需要為JobStore指定所使用的數據庫表前綴(前面討論過)。
配置AdoJobStore的數據庫表前綴
quartz.jobStore.tablePrefix = QRTZ
然后需要設置JobStore所使用的數據源。必須在Quartz屬性中定義已命名的數據源,比如,我們指定Quartz使用名為"default"的數據源(在配置文件的其他地方定義)。
配置 AdoJobStore使用數據源源的名字
properties["quartz.jobStore.dataSource"] = "default"
最后,需要配置數據源的使用的Ado.net數據提供者和數據庫連接串,數據庫連接串是標准的Ado.net 數據庫連接的連接串。數據庫提供者是關系數據庫同Quartz.net之間保持低耦合的數據庫的連接提供者.
配置AdoJobStore使用數據源源的數據庫連接串和數據庫提供者
quartz.dataSource.default.connectionString = Server=(local);Database=quartz;Trusted_Connection=True;
quartz.dataSource.default.provider= SqlServer-11
目前Quartz.net支持的以下數據庫的數據提供者:
l SqlServer-11 - SQL Server driver for .NET Framework 1.1
l SqlServer-20 - SQL Server driver for .NET Framework 2.0
l OracleClient-20 - Microsoft's oracle Driver (comes bundled with .NET Framework)
l OracleODP-20 - Oracle's Oracle Driver
l mysql-10 - MySQL Connector/.NET v. 1.0.7
l MySql-109 - MySQL Connector/.NET v. 1.0.9
l MySql-50 - MySQL Connector/.NET v. 5.0 (.NET 2.0)
l MySql-51 - MySQL Connector/:NET v. 5.1 (.NET 2.0)
l SQLite1044 - SQLite ADO.NET 2.0 Provider v. 1.0.44 (.NET 2.0)
如果Scheduler非常忙(比如,執行的任務數量差不多和線程池的數量相同,那么你需要正確地配置DataSource的連接數量為線程池數量。為了指示AdoJobStore所有的JobDataMaps中的值都是字符串,並且能以“名字-值”對的方式存儲而不是以復雜對象的序列化形式存儲在BLOB字段中,應設置 quartz.jobStore.usePropertiess配置參數的值為"true"(這是缺省的方式)。這樣做,從長遠來看非常安全,這樣避免了對存儲在BLOB中的非字符串的序列化對象的類型轉換問題。
清單 4 展示了 AdoJobStore提供的數據持久性。就像在前面的示例中一樣,先從初始化 SchedulerFactory 和 Scheduler 開始。然后,不再需要初始化作業和觸發器,而是要獲取觸發器群組名稱列表,之后對於每個群組名稱,獲取觸發器名稱列表。請注意,每個現有的作業都應當用 Scheduler. RescheduleJob () 方法重新調度。僅僅重新初始化在先前的應用程序運行時終止的作業,不會正確地裝載觸發器的屬性。
清單4 AdoJobStoreRunner.cs
- public class AdoJobStoreRunner : IExample
- {
- public string Name
- {
- get { return GetType().Name; }
- }
- private static ILog _log = LogManager.GetLogger(typeof(AdoJobStoreRunner));
- public virtual void CleanUp(IScheduler inScheduler)
- {
- _log.Warn("***** Deleting existing jobs/triggers *****");
- // unschedule jobs
- string[] groups = inScheduler.TriggerGroupNames;
- for (int i = 0; i < groups.Length; i++)
- {
- String[] names = inScheduler.GetTriggerNames(groups[i]);
- for (int j = 0; j < names.Length; j++)
- inScheduler.UnscheduleJob(names[j], groups[i]);
- }
- // delete jobs
- groups = inScheduler.JobGroupNames;
- for (int i = 0; i < groups.Length; i++)
- {
- String[] names = inScheduler.GetJobNames(groups[i]);
- for (int j = 0; j < names.Length; j++)
- inScheduler.DeleteJob(names[j], groups[i]);
- }
- }
- public virtual void Run(bool inClearJobs, bool inScheduleJobs)
- {
- NameValueCollection properties = new NameValueCollection();
- properties["quartz.scheduler.instanceName"] = "TestScheduler";
- properties["quartz.scheduler.instanceId"] = "instance_one";
- properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
- properties["quartz.threadPool.threadCount"] = "5";
- properties["quartz.threadPool.threadPriority"] = "Normal";
- properties["quartz.jobStore.misfireThreshold"] = "60000";
- properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
- properties["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz";
- properties["quartz.jobStore.useProperties"] = "false";
- properties["quartz.jobStore.dataSource"] = "default";
- properties["quartz.jobStore.tablePrefix"] = "QRTZ_";
- properties["quartz.jobStore.clustered"] = "true";
- // if running MS SQL Server we need this
- properties["quartz.jobStore.selectWithLockSQL"] = "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = @lockName";
- properties["quartz.dataSource.default.connectionString"] = @"Server=LIJUNNIN-PCSQLEXPRESS;Database=quartz;Trusted_Connection=True;";
- properties["quartz.dataSource.default.provider"] = "SqlServer-20";
- // First we must get a reference to a scheduler
- ISchedulerFactory sf = new StdSchedulerFactory(properties);
- IScheduler sched = sf.GetScheduler();
- if (inClearJobs)
- {
- CleanUp(sched);
- }
- _log.Info("------- Initialization Complete -----------");
- if (inScheduleJobs)
- {
- _log.Info("------- Scheduling Jobs ------------------");
- string schedId = sched.SchedulerInstanceId;
- int count = 1;
- JobDetail job = new JobDetail("job_" + count, schedId, typeof(SimpleQuartzJob));
- // ask scheduler to re-Execute this job if it was in progress when
- // the scheduler went down...
- job.RequestsRecovery = true;
- SimpleTrigger trigger = new SimpleTrigger("trig_" + count, schedId, 20, 5000L);
- trigger.StartTime = DateTime.Now.AddMilliseconds(1000L);
- sched.ScheduleJob(job, trigger);
- _log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.FullName, trigger.GetNextFireTime(), trigger.RepeatCount, (trigger.RepeatInterval / 1000)));
- count++;
- job = new JobDetail("job_" + count, schedId, typeof(SimpleQuartzJob));
- // ask scheduler to re-Execute this job if it was in progress when
- // the scheduler went down...
- job.RequestsRecovery = (true);
- trigger = new SimpleTrigger("trig_" + count, schedId, 20, 5000L);
- trigger.StartTime = (DateTime.Now.AddMilliseconds(2000L));
- sched.ScheduleJob(job, trigger);
- _log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.FullName, trigger.GetNextFireTime(), trigger.RepeatCount, (trigger.RepeatInterval / 1000)));
- count++;
- job = new JobDetail("job_" + count, schedId, typeof(SimpleQuartzJob));
- // ask scheduler to re-Execute this job if it was in progress when
- // the scheduler went down...
- job.RequestsRecovery = (true);
- trigger = new SimpleTrigger("trig_" + count, schedId, 20, 3000L);
- trigger.StartTime = (DateTime.Now.AddMilliseconds(1000L));
- sched.ScheduleJob(job, trigger);
- _log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.FullName, trigger.GetNextFireTime(), trigger.RepeatCount, (trigger.RepeatInterval / 1000)));
- count++;
- job = new JobDetail("job_" + count, schedId, typeof(SimpleQuartzJob));
- // ask scheduler to re-Execute this job if it was in progress when
- // the scheduler went down...
- job.RequestsRecovery = (true);
- trigger = new SimpleTrigger("trig_" + count, schedId, 20, 4000L);
- trigger.StartTime = (DateTime.Now.AddMilliseconds(1000L));
- sched.ScheduleJob(job, trigger);
- _log.Info(string.Format("{0} will run at: {1} & repeat: {2}/{3}", job.FullName, trigger.GetNextFireTime(), trigger.RepeatCount, trigger.RepeatInterval));
- count++;
- job = new JobDetail("job_" + count, schedId, typeof(SimpleQuartzJob));
- // ask scheduler to re-Execute this job if it was in progress when
- // the scheduler went down...
- job.RequestsRecovery = (true);
- trigger = new SimpleTrigger("trig_" + count, schedId, 20, 4500L);
- trigger.StartTime = (DateTime.Now.AddMilliseconds(1000L));
- sched.ScheduleJob(job, trigger);
- _log.Info(string.Format("{0} will run at: {1} & repeat: {2}/{3}", job.FullName, trigger.GetNextFireTime(), trigger.RepeatCount, trigger.RepeatInterval));
- }
- // jobs don't start firing until start() has been called...
- _log.Info("------- Starting Scheduler ---------------");
- sched.Start();
- _log.Info("------- Started Scheduler ----------------");
- _log.Info("------- Waiting for one hour... ----------");
- Thread.Sleep(TimeSpan.FromHours(1));
- _log.Info("------- Shutting Down --------------------");
- sched.Shutdown();
- _log.Info("------- Shutdown Complete ----------------");
- }
- public void Run()
- {
- bool clearJobs = true;
- bool scheduleJobs = true;
- AdoJobStoreRunner example = new AdoJobStoreRunner();
- example.Run(clearJobs, scheduleJobs);
- }
- }
結束語
Quartz.net 作業調度框架所提供的 API 在兩方面都表現極佳:既全面強大,又易於使用。Quartz 可以用於簡單的作業觸發,也可以用於復雜的 Ado.net持久的作業存儲和執行。
示例下載 :www.cnblogs.com/Files/shanyou/QuartzBeginnerExample.zip 基於 Quartz.net 的示例 (C#代碼 ) QuartzBeginnerExample.zip 324KB
獲取Quartz.net :quartznet.sourceforge.net/download.html:.NET應用程序的開放源碼作業調度解決方案
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/dz45693/archive/2011/01/18/6151189.aspx