詳細講解Quartz.NET

Quartz.NET是一個開源的做業調度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#寫成,可用於winform和asp.net應用中。它提供了巨大的靈活性而不犧牲簡單性。你可以用它來爲執行一個做業而建立簡單的或複雜的調度。它有不少特徵,如:數據庫支持,集羣,插件,支持cron-like表達式等等。html

你曾經須要應用執行一個任務嗎?這個任務天天或每週星期二晚上11:30,或許僅僅每月的最後一天執行。一個自動執行而無須干預的任務在執行過程當中若是發生一個嚴重錯誤,應用可以知到其執行失敗並嘗試從新執行嗎?你和你的團隊是用.NET編程嗎?若是這些問題中任何一個你回答是,那麼你應該使用Quartz.NET調度器。 Quartz.NET容許開發人員根據時間間隔(或天)來調度做業。它實現了做業和觸發器的多對多關係,還能把多個做業與不一樣的觸發器關聯。整合了 Quartz.NET的應用程序能夠重用來自不一樣事件的做業,還能夠爲一個事件組合多個做業.數據庫

Quartz.NET入門express

要開始使用 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>asp.net

<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持久的做業存儲和執行。

示例下載

http://www.cnblogs.com/Files/shanyou/QuartzBeginnerExample.zip

相關文章
相關標籤/搜索