正如標題所示,文章主要是圍繞Quartz.Net做業調度框架話題展開的,內容出自博主學習官方Examples的學習心得與體會,文中不免會有錯誤之處,還請指出得以指教。框架
在進入Quartz.Net以前,先來看一下Quartz.Net框架運行環境要準備的一些東西.ide
首先須要準備如下程序集:函數
1.Common.Logging學習
2.Common.Logging.Coreui
3.Common.Logging.Log4Net1213spa
4.log4net.net
5.Topshelfcode
6.Quartzorm
須要稍微注意一下彼此版本號要對應,博主的運行環境中依次版本號爲(僅供參考):Common.Logging 3.0.0.0 ,Common.Logging.Core 3.0.0.0,Common.Logging.Log4Net1213 3.0.0.0 ,log4net 1.2.13.0 ,Quartz 2.3.2.0 ,Topshelf 3.1.135.0對象
ps:博主這裏新建的控制檯程序Quartz.Examples,程序集版本.net framework 4.0
一:接下來進入主題,首先先來看一段代碼:
TIP:在接下來的所有例子中main函數都是使用如下代碼,因此在貼出代碼的時候將再也不重複貼出main函數相關代碼。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Quartz.Util; using System.Reflection; namespace Quartz.Examples { class Program { static void Main(string[] args) { try { //反射獲得當前運行程序集 Assembly asm = Assembly.GetExecutingAssembly(); //獲取全部類 Type[] types = asm.GetTypes(); IDictionary<int, Type> typeMap = new Dictionary<int, Type>(); int counter = 1; Console.WriteLine("Select example to run: "); List<Type> typeList = new List<Type>(); //循環遍歷當前運行程序集的全部類 foreach (Type t in types) { //將實現了IExample接口的類加入到typeList集合中 if (new List<Type>(t.GetInterfaces()).Contains(typeof(IExample))) { typeList.Add(t); } } typeList.Sort(new TypeNameComparer()); //循環將序號 類名 加入到typeMap字典中 foreach (Type t in typeList) { string counterString = string.Format("[{0}]", counter).PadRight(4); Console.WriteLine("{0} {1} {2}", counterString, t.Namespace.Substring(t.Namespace.LastIndexOf(".") + 1), t.Name); typeMap.Add(counter++, t); } Console.WriteLine(); Console.Write("> "); //選擇要運行的類的序號 int num = Convert.ToInt32(Console.ReadLine()); //獲取該類的Type Type eType = typeMap[num]; //獲得該類的實例 IExample example = ObjectUtils.InstantiateType<IExample>(eType); //運行Run() example.Run(); Console.WriteLine("Example run successfully."); } catch (Exception ex) { Console.WriteLine("Error running example: " + ex.Message); Console.WriteLine(ex.ToString()); } Console.Read(); } /// <summary> /// 用於排序的比較器 /// </summary> public class TypeNameComparer : IComparer<Type> { public int Compare(Type t1, Type t2) { if (t1.Namespace.Length > t2.Namespace.Length) { return 1; } if (t1.Namespace.Length < t2.Namespace.Length) { return -1; } return t1.Namespace.CompareTo(t2.Namespace); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Quartz.Examples { using Common.Logging; /// <summary> /// This is just a simple job that gets fired off many times by example 2. /// </summary> /// <author>Bill Kratzer</author> /// <author>Marko Lahma (.NET)</author> public class SimpleJob : IJob { private static readonly ILog log = LogManager.GetLogger(typeof(SimpleJob)); /// <summary> /// Called by the <see cref="IScheduler" /> when a /// <see cref="ITrigger" /> fires that is associated with /// the <see cref="IJob" />. /// </summary> public virtual void Execute(IJobExecutionContext context) { //這個做業演示簡單的打印出做業自己的名稱以及正在運行的日期和時間 JobKey jobKey = context.JobDetail.Key;//做業的名稱 String description=context.JobDetail.Description;//做業的描述 bool durable=context.JobDetail.Durable;//是否持續着 log.InfoFormat("SimpleJob says: {0} executing at {1} and Description is {2} and durable is {3}", jobKey, DateTime.Now.ToString("r"), description,durable); } } }
using System.Text; using Quartz.Impl; using Common.Logging; using System.Threading; namespace Quartz.Examples { public class YZRExample:IExample { #region IExample 成員 public string Name { get { return GetType().Name; } } public void Run() { ILog log = LogManager.GetLogger(typeof(SimpleTriggerExample)); log.Info("------- 開始初始化 -------------------"); // 使用工廠獲得調度實例 ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); log.Info("------- 初始化完成 --------"); log.Info("------- 調度做業 ----------------"); DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(DateTimeOffset.UtcNow.AddMilliseconds(5), 15); //做業1將會經過startTime指定觸發,而且只會運行一次 IJobDetail job = JobBuilder.Create<SimpleJob>() .WithIdentity("job1", "group1") .Build(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartAt(startTime) .Build(); //調度開始運行 DateTimeOffset? ft = sched.ScheduleJob(job, trigger); log.Info(job.Key + " will run at: " + ft + " and repeat: " + trigger.RepeatCount + " times, every " + trigger.RepeatInterval.TotalSeconds + " seconds"); log.Info("------- Starting Scheduler ----------------"); sched.Start(); log.Info("------- Started Scheduler -----------------"); log.Info("------- Waiting five minutes... ------------"); try { // wait five minutes to show jobs Thread.Sleep(TimeSpan.FromMinutes(5)); // 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.NumberOfJobsExecuted)); } #endregion } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Quartz.Examples { /// <summary> /// Interface for examples. /// </summary> /// <author>Marko Lahma (.NET)</author> public interface IExample { string Name { get; } void Run(); } }
運行的主要結果以下:
2016-04-23 01:30:04,760 [1] INFO Quartz.Examples.YZRExample.Run(C:\Users\Admini
strator\Desktop\Quatorz\Quartz.Examples\Quartz.Examples\YZRExample.cs:172)
- ------ Started Scheduler -----------------
2016-04-23 01:30:04,761 [1] INFO Quartz.Examples.YZRExample.Run(C:\Users\Administrator\Desktop\Quatorz\Quartz.Examples\Quartz.Examples\YZRExample.cs:232)
- ------ Waiting five minutes... ------------
2016-04-23 01:30:15,039 [DefaultQuartzScheduler_Worker-1] INFO Quartz.Examples.
simpleJob.Execute(C:\Users\Administrator\Desktop\Quatorz\Quartz.Examples\Quartz.
examples\SimpleJob.cs:31) -
SimpleJob says: group1.job1 executing at Sat, 23 Apr
2016 01:30:15 GMT and Description is and durable is False
從上面的部分打印信息可知,SimpleJob做業被調度了一次,而且是發生在第15秒這個時機。
你可能會疑惑,爲何在15秒的時候打印信息?
從頭至尾來看一下代碼:
// 使用工廠獲得調度實例 ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler();
在定義任何做業被調度以前,都須要使用StdSchedulerFactory()獲得一個IScheduler 調度實例,經過這個調度實例來添加做業隊列以及相對應的觸發器。
DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(DateTimeOffset.UtcNow.AddMilliseconds(5), 15);
DateTimeOffset NextGivenSecondDate(DateTimeOffset? date, int secondBase):
startTime定義的是做業第一次運行的時機,經過DateBuilder.NextGivenSecondDate(DateTimeOffset date,int secondBase);方法獲得這個時機,
這個方法具體的意思是指:
當date爲null時,startTime的值將會等於DateTime.Now當前時間
secondBase是指時間的第一個倍數,0<=secondBase<=59
好比舉個例子:
在程序運行後的一分鐘以後,第一個能整除15的秒數(15s,30s,45s,60s)觸發做業,那麼這個startTime能夠這麼聲明:
DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(DateTimeOffset.UtcNow.AddMinutes(1), 15);
UtcNow值得是時區時間,Now是本地時間(Now本質也是先取時區時間UtcNow再進行轉換成本地時間)
IJobDetail job = JobBuilder.Create<SimpleJob>() .WithIdentity("job1", "group1") .Build();
IJobDetail 接口定義的是做業類,每個做業類都有一個做業id和組id。
ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartAt(startTime) .Build();
ISimpleTrigger接口定義的是觸發器類,每個Tirgger都有一個觸發器id和組id。在觸發器中指定觸發的第一次時機。
//調度開始運行 DateTimeOffset? ft = sched.ScheduleJob(job, trigger); log.Info(job.Key + " will run at: " + ft + " and repeat: " + trigger.RepeatCount + " times, every " + trigger.RepeatInterval.TotalSeconds + " seconds");
調度實例sched指定做業類和觸發器添加到做業隊列中。
sched.Start();
須要sched.Start()纔會開始調度隊列中的做業。
二:如何指定一個做業重複調度的次數以及調度時間間隔呢?
博主認爲做業的如何調度是在Tirgger中指定的,而做業自己相關的是定義在Job中,從而可知,做業重複調度的次數以及調度時間間隔這些調度機制是在Tirgger中指定。將上面的tirgger稍微修改一下,代碼以下:
trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger3", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).WithRepeatCount(10))//每10秒運行一次,而且重複10次(運行一次重複10次,因此將會運行11次) .Build();
固然也能夠指定無限重複輪詢:
trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger6", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInSeconds(40).RepeatForever()) .Build();
三:調度實例sched手動加入觸發器
調度實例能夠手動加入觸發器,這樣只須要定義做業類:
//手動加入觸發器job8 job = JobBuilder.Create<SimpleJob>() .WithIdentity("job8", "group1") .StoreDurably() .Build(); sched.AddJob(job, true); log.Info("'Manually' triggering job8..."); sched.TriggerJob(new JobKey("job8", "group1"));
四:補充
1.當有多個做業IJobDetail時,能夠屢次添加到sched調度實例中,sched調度實例開啓以後,實際上會根據優先級來輪詢做業。
2.一個做業能夠包含多個觸發器,好比說,job1和tirgger1添加到sched以外,還能夠job1和tirgger2再添加到sched調度隊列中。
//這裏再演示一個job擁有多個trigger,使用ForJob("做業對象")來實現 //這一次將只會重複2次每隔10秒 trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger3", "group2") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).WithRepeatCount(2)) .ForJob(job)//爲做業添加tirgger .Build(); ft = sched.ScheduleJob(trigger);
3.能夠在定義觸發器的時候定義觸發時間startTime
//演示在trigger中指定觸發時間 job = JobBuilder.Create<SimpleJob>() .WithIdentity("job5", "group1") .Build(); trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger5", "group1") .StartAt(DateBuilder.FutureDate(1, IntervalUnit.Minute)) .Build(); ft = sched.ScheduleJob(job, trigger);
4.觸發器中使用RepeatForever能夠無限次輪詢
job = JobBuilder.Create<SimpleJob>() .WithIdentity("job6", "group1") .Build(); trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger6", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInSeconds(40).RepeatForever()) .Build(); ft = sched.ScheduleJob(job, trigger);
5.最後強調一下,當job和tirgger都定義好而且添加到調度實例中以後,須要開啓調度:
sched.Start();
只有start以後,調度實例中做業隊列纔會被開始調度。另外,在start()以後,還有繼續往調度實例中添加(做業觸發器),即便是在start以後添加到隊列中去的,只須要將tirgger設置爲從新啓動,相關的做業仍是會被正常調度。
// jobs can be re-scheduled... // job 7 will run immediately and repeat 10 times for every second log.Info("------- Rescheduling... --------------------"); trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger7", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInMinutes(5).WithRepeatCount(20)) .Build(); ft = sched.RescheduleJob(trigger.Key, trigger);//將觸發器指定爲該trigger("trigger7", "group1")的做業將會被從新啓動
6.移出執行計劃
ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); var tirgger = new TriggerKey("TirggerName", "TirggerGroup"); sched.PauseTrigger(tirgger); var job = new JobKey("jobName","jobGroup"); sched.PauseJob(job);
sched.DeleteJob(JobKey.Create("jobName", "jobGroup"));