正如標題所示,文章主要是圍繞Quartz.Net做業調度框架話題展開的,內容出自博主學習官方Examples的學習心得與體會,文中不免會有錯誤之處,還請指出得以指教。java
本篇關於Quart.Net涉及到的關鍵詞是參數傳遞,狀態維持,異常處理。框架
一:傳參less
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using Quartz.Impl; using Common.Logging; namespace Quartz.Examples { public class YZRExample3:IExample { #region IExample 成員 public string Name { get { return GetType().Name; } } public void Run() { ILog log = LogManager.GetLogger(typeof(JobStateExample)); log.Info("------- 初始化 -------------------"); // First we must get a reference to a scheduler ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); log.Info("------- 初始化完成 --------"); log.Info("------- 開始調度隊列進程 ----------------"); DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 10); IJobDetail job1 = JobBuilder.Create<ColorJob>() .WithIdentity("job1", "group1") .Build(); ISimpleTrigger trigger1 = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).WithRepeatCount(4)) .Build(); //將初始化參數傳遞到做業中 job1.JobDataMap.Put(ColorJob.FavoriteColor, "Green"); job1.JobDataMap.Put(ColorJob.ExecutionCount, 1); DateTimeOffset scheduleTime1 = sched.ScheduleJob(job1, trigger1); log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job1.Key, scheduleTime1.ToString("r"), trigger1.RepeatCount, trigger1.RepeatInterval.TotalSeconds)); 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 60 seconds... -------------"); 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.NumberOfJobsExecuted)); } #endregion } }
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 receives parameters and maintains state. /// 演示一個簡單的傳接參以及狀態保持===>主程序能夠經過jobDataMap初始化參數,job類經過JobDataMap來獲取參數,而且狀態保持。 /// </summary> /// <author>Bill Kratzer</author> /// <author>Marko Lahma (.NET)</author> [PersistJobDataAfterExecution] [DisallowConcurrentExecution] public class ColorJob : IJob { private static readonly ILog log = LogManager.GetLogger(typeof(ColorJob)); // parameter names specific to this job public const string FavoriteColor = "favorite color"; public const string ExecutionCount = "count"; // Since Quartz will re-instantiate a class every time it // gets executed, members non-static member variables can // not be used to maintain state! private int counter = 1; /// <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) { // This job simply prints out its job name and the // date and time that it is running JobKey jobKey = context.JobDetail.Key; // Grab and print passed parameters JobDataMap data = context.JobDetail.JobDataMap; string favoriteColor = data.GetString(FavoriteColor); int count = data.GetInt(ExecutionCount); log.InfoFormat( "ColorJob: {0} executing at {1}\n favorite color is {2}\n execution count (from job map) is {3}\n execution count (from job member variable) is {4}", jobKey, DateTime.Now.ToString("r"), favoriteColor, count, counter); // increment the count and store it back into the job map so that job state can be properly maintained //增長計數並將其儲存在job map中,以使job state獲得適當的維護 count++; data.Put(ExecutionCount, count); // Increment the local member variable //增量本地成員變量 // This serves no real purpose since job state can not be maintained via member variables! // 這並非真正的目的,由於job state不能經過成員變量來保持! counter++; } } }
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(); } }
對於以上代碼還有不熟悉的,建議請先看前面幾章的介紹,這有助於你理解接下來討論的話題。ide
上述代碼咱們構建了一個簡單的輪詢調度做業,惟一隻有兩行代碼比較陌生新鮮:學習
//將初始化參數傳遞到做業中 job1.JobDataMap.Put(ColorJob.FavoriteColor, "Green"); job1.JobDataMap.Put(ColorJob.ExecutionCount, 1);
IJob接口下定義了public virtual void Execute(IJobExecutionContext context);方法,注意到這個方法定義了一個IJobExecutionContext類型的參數,能夠經過前面的幾個例子知道這個context能夠獲得:ui
JobKey jobKey = context.JobDetail.Key;//做業的名稱
String description=context.JobDetail.Description;//做業的描述
bool durable=context.JobDetail.Durable;this
這裏還能夠獲得另一個,沒錯沒就是JobDataMap(Key-Value):spa
JobDataMap data = context.JobDetail.JobDataMap;3d
咱們能夠經過JobDataMap來傳送參數給做業類,而且JobDataMap裏的參數鍵值對會獲得狀態保持。並且,上面例子也說明了在做業類定義成員變量將不會獲得狀態維持。code
PS:Map在java中是一個接口類型,有HashMap,TreeMap,它們自己都是鍵值對的形式保存數據。
除了job1.JobDataMap.Put()這種顯示的方式傳送參數以外,還可使用UsingJobData()來傳遞參數給做業類:
在定義做業類的時候使用UsingJobData()傳遞參數:
IJobDetail job = JobBuilder.Create<ColorJob>() .WithIdentity("statefulJob1", "group1") .UsingJobData(StatefulDumbJob.ExecutionDelay, 10000L) .Build();
在ColorJob中JobDataMap data = context.JobDetail.JobDataMap; data會包含使用UsingJobData()傳遞的參數:
二:做業與做業(一樣的做業)之間參數以及狀態是否會互相干擾?
添加多一個做業ColorJob,而且也傳送參數,代碼以下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using Quartz.Impl; using Common.Logging; namespace Quartz.Examples { public class YZRExample3:IExample { #region IExample 成員 public string Name { get { return GetType().Name; } } public void Run() { ILog log = LogManager.GetLogger(typeof(JobStateExample)); log.Info("------- 初始化 -------------------"); // First we must get a reference to a scheduler ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); log.Info("------- 初始化完成 --------"); log.Info("------- 開始調度隊列進程 ----------------"); // get a "nice round" time a few seconds in the future.... DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 10); // job1 will only run 5 times (at start time, plus 4 repeats), every 10 seconds IJobDetail job1 = JobBuilder.Create<ColorJob>() .WithIdentity("job1", "group1") .Build(); ISimpleTrigger trigger1 = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).WithRepeatCount(4)) .Build(); // pass initialization parameters into the job //將初始化參數傳遞到做業中 job1.JobDataMap.Put(ColorJob.FavoriteColor, "Green"); job1.JobDataMap.Put(ColorJob.ExecutionCount, 1); // schedule the job to run DateTimeOffset scheduleTime1 = sched.ScheduleJob(job1, trigger1); log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job1.Key, scheduleTime1.ToString("r"), trigger1.RepeatCount, trigger1.RepeatInterval.TotalSeconds)); //做業與做業之間的狀態是分開的,不會互相干擾 // job2 will also run 5 times, every 10 seconds IJobDetail job2 = JobBuilder.Create<ColorJob>() .WithIdentity("job2", "group1") .Build(); ISimpleTrigger trigger2 = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger2", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).WithRepeatCount(4)) .Build(); // pass initialization parameters into the job // this job has a different favorite color! job2.JobDataMap.Put(ColorJob.FavoriteColor, "Red"); job2.JobDataMap.Put(ColorJob.ExecutionCount, 1); // schedule the job to run DateTimeOffset scheduleTime2 = sched.ScheduleJob(job2, trigger2); log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job2.Key, scheduleTime2.ToString("r"), trigger2.RepeatCount, trigger2.RepeatInterval.TotalSeconds)); 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 60 seconds... -------------"); 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.NumberOfJobsExecuted)); } #endregion } }
當你運行代碼看了打印信息以後,就能夠得知,做業與做業之間是相互區分的,不會互相干擾。
三:延遲執行
存在一種可能就是在輪詢的時間週期很短,但咱們的做業執行須要花費的時間較長,這樣的狀況下咱們須要讓下一次輪詢的時機到來之時先等待做業執行完畢,再觸發下一次時機。
這樣子會致使的結果是:假如做業執行花費的時間爲10s,而程序定製的輪詢週期爲每5s一次,這樣等價於下一次輪詢到的時機到來時上一次做業還沒
來得及完成,因此下一次觸發必須先等待做業完成以後再觸發。很明顯結果就是輪詢週期再也不是指定的每5s一次,而是10s一次。
//延遲10秒再繼續重複做業 IJobDetail job = JobBuilder.Create<StatefulDumbJob>() .WithIdentity("statefulJob1", "group1") .UsingJobData(StatefulDumbJob.ExecutionDelay, 10000L) .Build(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever()) .Build(); DateTimeOffset ft = sched.ScheduleJob(job, trigger); log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, ft.ToString("r"), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds)); */ //比較合適的寫法是:當沒有在輪詢時機觸發時使用WithMisfireHandlingInstructionNowWithExistingCount() // statefulJob2 will run every three seconds // (but it will delay for ten seconds - and therefore purposely misfire after a few iterations) IJobDetail job = JobBuilder.Create<StatefulDumbJob>() .WithIdentity("statefulJob2", "group1") .UsingJobData(StatefulDumbJob.ExecutionDelay, 10000L) .Build(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger2", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x .WithIntervalInSeconds(3) .RepeatForever() .WithMisfireHandlingInstructionNowWithExistingCount()) // set misfire instructions .Build(); DateTimeOffset ft = sched.ScheduleJob(job, trigger); log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, ft.ToString("r"), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds));
四:異常JobExecutionException
1.發生異常時程序會拋出異常信息,而且恢復繼續調度
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Common.Logging; namespace Quartz.Examples { /// <summary> /// A job dumb job that will throw a job execution exception. /// 異常處理: /// 在做業發生異常的時候,拋出一個異常,而且程序對異常進行處理以後,RefireImmediately會立刻恢復做業的繼續調度 /// </summary> /// <author>Bill Kratzer</author> /// <author>Marko Lahma (.NET)</author> [PersistJobDataAfterExecution] [DisallowConcurrentExecution] public class BadJob1 : IJob { // Logging private static readonly ILog log = LogManager.GetLogger(typeof(BadJob1)); /// <summary> /// Called by the <see cref="IScheduler" /> when a Trigger" /> /// fires that is associated with the <see cref="IJob" />. /// </summary> public virtual void Execute(IJobExecutionContext context) { JobKey jobKey = context.JobDetail.Key; JobDataMap dataMap = context.JobDetail.JobDataMap; int denominator = dataMap.GetInt("denominator"); log.InfoFormat("{0} with denominator {1}", "---" + jobKey + " executing at " + DateTime.Now, denominator); // a contrived example of an exception that // will be generated by this job due to a // divide by zero error (only on first run) try { int calculation = 4815 / denominator; } catch (Exception e) { log.Info("--- Error in job!"); JobExecutionException e2 = new JobExecutionException(e); // fix denominator so the next time this job run // it won't fail again dataMap.Put("denominator", "1"); // this job will refire immediately e2.RefireImmediately = true; throw e2; } log.InfoFormat("---{0} completed at {1}", jobKey, DateTime.Now.ToString("r")); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Common.Logging; using Quartz.Impl; using System.Threading; namespace Quartz.Examples { public class YZRExample5:IExample { #region IExample 成員 public string Name { get { return GetType().Name; } } public void Run() { ILog log = LogManager.GetLogger(typeof(JobExceptionExample)); 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 -------------------"); DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 15); //模擬做業內部發生異常,但進過異常處理以後,做業會繼續進行調度 IJobDetail job = JobBuilder.Create<BadJob1>() .WithIdentity("badJob1", "group1") .UsingJobData("denominator", "0")//使用除數爲0模擬異常 .Build(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).RepeatForever()) .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 -----------------"); // sleep for 30 seconds try { Thread.Sleep(TimeSpan.FromSeconds(30)); } catch (ThreadInterruptedException) { } log.Info("------- Shutting Down ---------------------"); sched.Shutdown(false); log.Info("------- Shutdown Complete -----------------"); SchedulerMetaData metaData = sched.GetMetaData(); log.Info(string.Format("Executed {0} jobs.", metaData.NumberOfJobsExecuted)); } #endregion } }
2.發生異常時會拋出異常信息,而且中止調度
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Common.Logging; namespace Quartz.Examples { /// <summary> /// A job dumb job that will throw a job execution exception. /// 拋出異常,而且與當前做業有關的全部trigger都將會被中止調度 /// </summary> /// <author>Bill Kratzer</author> /// <author>Marko Lahma (.NET)</author> [PersistJobDataAfterExecution] [DisallowConcurrentExecution] public class BadJob2 : IJob { // Logging private static readonly ILog log = LogManager.GetLogger(typeof(BadJob2)); /// <summary> /// Called by the <see cref="IScheduler" /> when a <see cref="ITrigger" /> /// fires that is associated with the <see cref="IJob" />. /// <para> /// The implementation may wish to set a result object on the /// JobExecutionContext before this method exits. The result itself /// is meaningless to Quartz, but may be informative to /// <see cref="IJobListener" />s or /// <see cref="ITriggerListener" />s that are watching the job's /// execution. /// </para> /// </summary> /// <param name="context">Execution context.</param> public virtual void Execute(IJobExecutionContext context) { JobKey jobKey = context.JobDetail.Key; log.InfoFormat("---{0} executing at {1}", jobKey, System.DateTime.Now.ToString("r")); // a contrived example of an exception that will be generated by this job due to a divide by zero error // 在一個做業里人爲的拋出一個異常 // try { int zero = 0; int calculation = 4815 / zero; } catch (System.Exception e) { log.Info("--- Error in job!"); JobExecutionException e2 = new JobExecutionException(e); // Quartz will automatically unschedule // all triggers associated with this job // so that it does not run again e2.UnscheduleAllTriggers = true; throw e2; } log.InfoFormat("---{0} completed at {1}", jobKey, System.DateTime.Now.ToString("r")); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Common.Logging; using Quartz.Impl; using System.Threading; namespace Quartz.Examples { public class YZRExample5:IExample { #region IExample 成員 public string Name { get { return GetType().Name; } } public void Run() { ILog log = LogManager.GetLogger(typeof(JobExceptionExample)); 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 -------------------"); DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 15); //引發異常的做業將會中止全部相關的trigger的運行而且拋出異常 job = JobBuilder.Create<BadJob2>() .WithIdentity("badJob2", "group1") .Build(); trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger2", "group1") .StartAt(startTime) .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever()) .Build(); ft = sched.ScheduleJob(job, trigger); log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, ft.ToString("r"), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds)); log.Info("------- Starting Scheduler ----------------"); sched.Start(); log.Info("------- Started Scheduler -----------------"); // sleep for 30 seconds try { Thread.Sleep(TimeSpan.FromSeconds(30)); } catch (ThreadInterruptedException) { } log.Info("------- Shutting Down ---------------------"); sched.Shutdown(false); log.Info("------- Shutdown Complete -----------------"); SchedulerMetaData metaData = sched.GetMetaData(); log.Info(string.Format("Executed {0} jobs.", metaData.NumberOfJobsExecuted)); } #endregion } }