Quartz.NET 任務(wù)調(diào)度的核心元素是 scheduler, trigger 和 job,其中 trigger(用于定義調(diào)度時(shí)間的元素,即按照什么時(shí)間規(guī)則去執(zhí)行任務(wù)) 和 job 是任務(wù)調(diào)度的元數(shù)據(jù),scheduler 是實(shí)際執(zhí)行調(diào)度的控制器。在Quartz.NET中主要有兩種類(lèi)型的 job:無(wú)狀態(tài)的(stateless)和有狀態(tài)的(stateful)。對(duì)于同一個(gè) trigger 來(lái)說(shuō),有狀態(tài)的 job 不能被并行執(zhí)行,只有上一次觸發(fā)的任務(wù)被執(zhí)行完之后,才能觸發(fā)下一次執(zhí)行。無(wú)狀態(tài)任務(wù)一般指可以并發(fā)的任務(wù),即任務(wù)之間是獨(dú)立的,不會(huì)互相干擾。一個(gè) job 可以被多個(gè) trigger 關(guān)聯(lián),但是一個(gè) trigger 只能關(guān)聯(lián)一個(gè) job。某些任務(wù)需要對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行增刪改處理 , 這些任務(wù)不能并發(fā)執(zhí)行,就需要用到無(wú)狀態(tài)的任務(wù) , 否則會(huì)造成數(shù)據(jù)混亂。
另外有些情況下,我們需要將任務(wù)保存到數(shù)據(jù)庫(kù)中,特別是有些任務(wù)中包含參數(shù),例如累加的任務(wù),如果可以保存到數(shù)據(jù)庫(kù)中,即便中間斷電或者程序異常重啟,中間計(jì)算的結(jié)果也不會(huì)丟失,可以從斷點(diǎn)的結(jié)果進(jìn)行運(yùn)算(首先恢復(fù)任務(wù)),下面介紹一下如何用AdoJobStore將任務(wù)保存到SQL Server數(shù)據(jù)庫(kù)中.
事先要在數(shù)據(jù)庫(kù)上新建一個(gè)QRTZ_數(shù)據(jù)庫(kù),并執(zhí)行SQL建表腳本:
是一個(gè)無(wú)狀態(tài)的任務(wù),代碼如下:
1 using System; 2 using System.Collections.Specialized; 3 using System.Threading; 4 using Common.Logging; 5 using Quartz; 6 using Quartz.Impl; 7 using Quartz.Job; 8 using System.Windows.Forms; 9 namespace QuartzDemo10 {11 /// <summary>12 /// 無(wú)狀態(tài)的可恢復(fù)的任務(wù)13 /// </summary>14 public class RecoveryJob : IJob15 {16 17 PRivate const string Count = "count";18 public virtual void Execute(IJobExecutionContext context)19 {20 21 JobKey jobKey = context.JobDetail.Key;22 if (isOpen("FrmConsole"))23 {24 try25 {26 //獲取當(dāng)前Form1實(shí)例27 __instance = (FrmConsole)application.OpenForms["FrmConsole"];28 // 如果任務(wù)是恢復(fù)的任務(wù)的話29 if (context.Recovering)30 {31 __instance.SetInfo(string.Format("{0} RECOVERING at {1}", jobKey, DateTime.Now.ToString("r")));32 }33 else34 {35 __instance.SetInfo(string.Format("{0} starting at {1}", jobKey, DateTime.Now.ToString("r")));36 }37 38 JobDataMap data = context.JobDetail.JobDataMap;39 int count;40 if (data.ContainsKey(Count))41 {42 //是否能從數(shù)據(jù)庫(kù)中恢復(fù),如果保存Job等信息的話,程序運(yùn)行突然終端(可用調(diào)試時(shí)中斷運(yùn)行,而不是關(guān)閉窗體來(lái)模擬)43 count = data.GetInt(Count);44 }45 else46 {47 count = 0;48 }49 count++;50 data.Put(Count, count);51 52 __instance.SetInfo(string.Format(" {0} Count #{1}", jobKey, count));53 }54 catch (Exception ex)55 {56 Console.WriteLine(ex.Message);57 }58 }59 }60 61 62 private static FrmConsole __instance = null;63 64 /// <summary>65 /// 判斷窗體是否打開(kāi)66 /// </summary>67 /// <param name="appName"></param>68 /// <returns></returns>69 private bool isOpen(string appName)70 {71 FormCollection collection = Application.OpenForms;72 foreach (Form form in collection)73 {74 if (form.Name == appName)75 {76 return true;77 }78 }79 return false;80 }81 82 }83 }
是一個(gè)有狀態(tài)的任務(wù),和無(wú)狀態(tài)的區(qū)別就是在任務(wù)類(lèi)的上面用[PersistJobDataAfterExecution]標(biāo)注任務(wù)是有狀態(tài)的 , 有狀態(tài)的任務(wù)不允許并發(fā)執(zhí)行,也需要標(biāo)注 [DisallowConcurrentExecution],代碼如下:
1 using System; 2 using System.Collections.Specialized; 3 using System.Threading; 4 using Common.Logging; 5 using Quartz; 6 using Quartz.Impl; 7 using Quartz.Job; 8 using System.Windows.Forms; 9 namespace QuartzDemo10 {11 /// <summary>12 /// 用這個(gè)[PersistJobDataAfterExecution]標(biāo)注任務(wù)是有狀態(tài)的,13 /// 有狀態(tài)的任務(wù)不允許并發(fā)執(zhí)行 [DisallowConcurrentExecution]14 /// </summary>15 [PersistJobDataAfterExecution]16 [DisallowConcurrentExecution]17 public class RecoveryStatefulJob : RecoveryJob18 {19 20 }21 }
用 properties["quartz.dataSource.default.connectionString"] = "Server=(local);Database=QRTZ_;Trusted_Connection=True;";定義了數(shù)據(jù)庫(kù)的連接信息,程序運(yùn)行時(shí)會(huì)自動(dòng)將任務(wù)保存到數(shù)據(jù)庫(kù)中:
1 using System; 2 using System.Collections.Specialized; 3 using System.Threading; 4 using Common.Logging; 5 using Quartz; 6 using Quartz.Impl; 7 using Quartz.Job; 8 using System.Windows.Forms; 9 namespace QuartzDemo 10 { 11 /// <summary> 12 /// AdoJobStore的用法示例 13 /// </summary> 14 public class AdoJobStoreExample 15 { 16 public virtual void Run(bool inClearJobs, bool inScheduleJobs) 17 { 18 NameValueCollection properties = new NameValueCollection(); 19 20 properties["quartz.scheduler.instanceName"] = "TestScheduler"; 21 properties["quartz.scheduler.instanceId"] = "instance_one"; 22 properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz"; 23 properties["quartz.threadPool.threadCount"] = "5"; 24 properties["quartz.threadPool.threadPriority"] = "Normal"; 25 properties["quartz.jobStore.misfireThreshold"] = "60000"; 26 properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"; 27 properties["quartz.jobStore.useProperties"] = "false"; 28 properties["quartz.jobStore.dataSource"] = "default"; 29 properties["quartz.jobStore.tablePrefix"] = "QRTZ_"; 30 properties["quartz.jobStore.clustered"] = "true"; 31 // SQLite 32 // properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz"; 33 properties["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz"; 34 // 數(shù)據(jù)庫(kù)連接字符串 35 properties["quartz.dataSource.default.connectionString"] = "Server=(local);Database=QRTZ_;Trusted_Connection=True;"; 36 properties["quartz.dataSource.default.provider"] = "SqlServer-20"; 37 38 // First we must get a reference to a scheduler 39 ISchedulerFactory sf = new StdSchedulerFactory(properties); 40 IScheduler sched = sf.GetScheduler(); 41 42 bool b是否恢復(fù) = false; 43 if (inClearJobs) 44 { 45 Console.WriteLine("***** Deleting existing jobs/triggers *****"); 46 // sched.Clear(); 47 } 48 49 50 if (inScheduleJobs) 51 { 52 53 string schedId = sched.SchedulerInstanceId; 54 55 int count = 1; 56 57 //定義一個(gè)無(wú)狀態(tài)的任務(wù) 58 IJobDetail job = JobBuilder.Create<RecoveryJob>() 59 .WithIdentity("recoveryjob_" + count, schedId) 60 .RequestRecovery() //recovery 61 .Build(); 62 63 64 ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() 65 .WithIdentity("triger_" + count, schedId) 66 .StartAt(DateBuilder.FutureDate(1, IntervalUnit.Second)) 67 .WithSimpleSchedule(x => x.WithRepeatCount(20).WithInterval(TimeSpan.FromSeconds(3))) 68 .Build(); 69 //可用此來(lái)查看定義的觸發(fā)器觸發(fā)規(guī)則 70 //log.InfoFormat("{0} will run at: {1} and repeat: {2} times, every {3} seconds", 71 //job.Key, trigger.GetNextFireTimeUtc(), 72 //trigger.RepeatCount, 73 //trigger.RepeatInterval.TotalSeconds); 74 try 75 { 76 //如果數(shù)據(jù)庫(kù)已經(jīng)存在同名job和trigger,則綁定失敗 77 sched.ScheduleJob(job, trigger); 78 } 79 catch 80 { 81 b是否恢復(fù) = true; 82 } 83 count++; 84 85 //定義一個(gè)有狀態(tài)的任務(wù)*********************************************************** 86 job = JobBuilder.Create<RecoveryStatefulJob>() 87 .WithIdentity("Statefuljob_" + count, schedId) 88 .RequestRecovery() // recovery 89 .Build(); 90 91 trigger = (ISimpleTrigger)TriggerBuilder.Create() 92 .WithIdentity("triger_" + count, schedId) 93 .StartAt(DateBuilder.FutureDate(1, IntervalUnit.Second)) 94 .WithSimpleSchedule(x => x.WithRepeatCount(20).WithInterval(TimeSpan.FromSeconds(3))) 95 .Build(); 96 97 try 98 { 99 sched.ScheduleJob(job, trigger);100 }101 catch102 {103 b是否恢復(fù) = true;104 }105 106 107 108 }109 110 //啟動(dòng)111 sched.Start();112 //sched.Shutdown();113 114 }115 116 public string Name117 {118 get { return GetType().Name; }119 }120 121 public void Run()122 {123 bool clearJobs = true;124 //clearJobs = false;125 bool scheduleJobs = true;126 AdoJobStoreExample example = new AdoJobStoreExample();127 example.Run(clearJobs, scheduleJobs);128 }129 }130 }
可以看到有狀態(tài)的計(jì)數(shù)每次累加1,而無(wú)狀態(tài)的每次執(zhí)行時(shí)都會(huì)丟失累加數(shù)(新的實(shí)例),中斷程序,查看數(shù)據(jù)庫(kù)的QRTZ_JOB_DETAILS表,可以看見(jiàn)還有一個(gè)持久化的任務(wù):
中斷程序后(調(diào)試狀態(tài)時(shí)不關(guān)閉窗體,而是中斷調(diào)試,模擬異常關(guān)閉) ,再重新運(yùn)行可以看到如下界面:
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注