前言
quartz.net作業排程框架是偉大組織OpenSymphony開發的quartz scheduler專案的.net延伸移植版本。支援 cron-like表示式,叢集,資料庫。功能效能強大更不用說。
下載專案文件官網:http://www.quartz-scheduler.net/
專案中需引用:Common.Logging.dll , Common.Logging.Core.dll , Quartz.dll
下面給大家分解下我最近做的關於計劃排程的一個小專案,來輔助理解quartz.net的功能和常用方法。
quartz.net的簡單用法 -入門
如果你是quartz.net的使用新手,控制檯入門這裡,建議跟著做下,那麼10分鐘搞懂quartz.net也是 so easy 的事.
1.建立一個每隔3秒鐘執行一次的計劃排程
public class RunMain { static void Main(string[] args) { Console.WriteLine(DateTime.Now.ToString("r")); //1.首先建立一個作業排程池 ISchedulerFactory schedf = new StdSchedulerFactory(); IScheduler sched = schedf.GetScheduler(); //2.建立出來一個具體的作業 IJobDetail job = JobBuilder.Create<JobDemo>().Build(); //3.建立並配置一個觸發器 ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create().WithSimpleSchedule(x=>x.WithIntervalInSeconds(3).WithRepeatCount(int.MaxValue)).Build(); //4.加入作業排程池中 sched.ScheduleJob(job, trigger); //5.開始執行 sched.Start(); Console.ReadKey(); } } public class JobDemo : IJob { /// <summary> /// 這裡是作業排程每次定時執行方法 /// </summary> /// <param name="context"></param> public void Execute(IJobExecutionContext context) { Console.WriteLine(DateTime.Now.ToString("r")); } }
Note:1、記下作業排程建立的順序。2、上述程式碼執行結果是,每三秒執行一次JobDemo中的Execute,如果程式不停止,無休無止執行到天荒地老,呵呵,扯下蛋啊。
2.改進(豐富排程計劃):上一個作業,我想讓他每三秒執行一次,一共執行100次,開始執行時間設定在當前時間,結束時間我設定在2小時後,不過100次執行完沒2小時候都不再執行。
![](https://i.iter01.com/images/86153736c3d440f2e7d2df2432d97112d2ef05f0cb7522cf2307c3de9447249a.gif)
public class RunMain { static void Main(string[] args) { Console.WriteLine(DateTime.Now.ToString("r")); //首先建立一個作業排程池 ISchedulerFactory schedf = new StdSchedulerFactory(); IScheduler sched = schedf.GetScheduler(); //建立出來一個具體的作業 IJobDetail job = JobBuilder.Create<JobDemo>().Build(); //NextGivenSecondDate:如果第一個引數為null則表名當前時間往後推遲2秒的時間點。 DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddSeconds(1), 2); DateTimeOffset endTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddHours(2), 3); //建立並配置一個觸發器 ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create().StartAt(startTime).EndAt(endTime) .WithSimpleSchedule(x=>x.WithIntervalInSeconds(3).WithRepeatCount(100)) .Build(); //加入作業排程池中 sched.ScheduleJob(job, trigger); //開始執行 sched.Start(); Console.ReadKey(); } } public class JobDemo : IJob { /// <summary> /// 這裡是作業排程每次定時執行方法 /// </summary> /// <param name="context"></param> public void Execute(IJobExecutionContext context) { Console.WriteLine(DateTime.Now.ToString("r")); } }
3.繼續改進(cron-like使用):前兩個作業排程都太簡單,如果我想在每小時的第10,20,25,26,33,54分鐘,每分鐘的第1,10,14秒執行一次。那麼上面顯然是不能滿足的。這是我就把cron-like表示式引入進來,以實現各種時間緯度的呼叫。
public class RunMain { static void Main(string[] args) { Console.WriteLine(DateTime.Now.ToString("r")); //首先建立一個作業排程池 ISchedulerFactory schedf = new StdSchedulerFactory(); IScheduler sched = schedf.GetScheduler(); //建立出來一個具體的作業 IJobDetail job = JobBuilder.Create<JobDemo>().Build(); //NextGivenSecondDate:如果第一個引數為null則表名當前時間往後推遲2秒的時間點。 DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddSeconds(1), 2); DateTimeOffset endTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddYears(2), 3); //建立並配置一個觸發器 ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create().StartAt(startTime).EndAt(endTime) .WithCronSchedule("1,10,14 10,20,25,26,33,54 * * * ? ") .Build(); //加入作業排程池中 sched.ScheduleJob(job, trigger); //開始執行 sched.Start(); //掛起2天 Thread.Sleep(TimeSpan.FromDays(2)); //2天后關閉作業排程,將不在執行 sched.Shutdown(); Console.ReadKey(); } } public class JobDemo : IJob { /// <summary> /// 這裡是作業排程每次定時執行方法 /// </summary> /// <param name="context"></param> public void Execute(IJobExecutionContext context) { Console.WriteLine(DateTime.Now.ToString("r")); } }
基於Quartz.net的作業排程專案詳解
最終效果如開篇的第一個圖所示。
下面主要說說,作業排程的中怎麼定位到具體的作業排程,並給作業排程分組,命名,新增,啟動,停止。
首先展示下表結構,專案中我叫作業排程為任務排程。
1、新增作業排程。
/// <summary> /// 任務計劃 /// </summary> public static IScheduler scheduler = null; public static IScheduler GetScheduler() { if (scheduler != null) { return scheduler; } else { ISchedulerFactory schedf = new StdSchedulerFactory(); IScheduler sched = schedf.GetScheduler(); return sched; } } /// <summary> /// 新增任務計劃 /// </summary> /// <returns></returns> public bool AddScheduleJob(WJ_ScheduleEntity m) { try { if (m != null) { if (m.StarRunTime == null) { m.StarRunTime = DateTime.Now; } DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(m.StarRunTime, 1); if (m.EndRunTime == null) { m.EndRunTime = DateTime.MaxValue.AddDays(-1); } DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(m.EndRunTime, 1); scheduler = GetScheduler(); IJobDetail job = JobBuilder.Create<HttpJob>() .WithIdentity(m.JobName, m.JobGroup) .Build(); ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create() .StartAt(starRunTime) .EndAt(endRunTime) .WithIdentity(m.JobName, m.JobGroup) .WithCronSchedule(m.CronStr) .Build(); scheduler.ScheduleJob(job, trigger); scheduler.Start(); StopScheduleJob(m.JobGroup, m.JobName); return true; } return false; } catch (Exception ex) { //DoApplication.WriteLogFile(ex.Message + "\r\n" + ex.StackTrace); return false; } }
Note:1.這裡作業排程執行的函式如下。2.上面的WithIdentity(m.JobName, m.JobGroup) ,是給作業排程加入組,和名稱,方便我們針對哪一個作業計劃,進行啟動停止等操作。
![](https://i.iter01.com/images/86153736c3d440f2e7d2df2432d97112d2ef05f0cb7522cf2307c3de9447249a.gif)
public class HttpJob : IJob { public void Execute(IJobExecutionContext context) { ThreadPool.QueueUserWorkItem(delegate(Object o) { try { //DoApplication.WriteLogFile(context.JobDetail.Key.Group + "---" + context.JobDetail.Key.Name + "---" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "---" + context.NextFireTimeUtc.Value.DateTime.AddHours(8).ToString("yyyy-MM-dd HH:mm:ss")); var sm = new WJ_ScheduleManage().GetScheduleModel(new WJ_ScheduleEntity() { JobGroup = context.JobDetail.Key.Group, JobName = context.JobDetail.Key.Name }); new WJ_ScheduleManage().UpdateScheduleRunStatus(new WJ_ScheduleEntity() { JobGroup = context.JobDetail.Key.Group, JobName = context.JobDetail.Key.Name,RunStatus=(int)ADJ.Job.Entity.EnumType.JobRunStatus.執行中 }); ESBRequest req = new ESBRequest(sm.ServiceCode, sm.ApiCode); DataResult result = req.Request(); new WJ_ScheduleManage().UpdateScheduleRunStatus(new WJ_ScheduleEntity() { JobGroup = context.JobDetail.Key.Group, JobName = context.JobDetail.Key.Name, RunStatus = (int)ADJ.Job.Entity.EnumType.JobRunStatus.待執行 }); if (result.Code == 1) { #region 加入執行明細 WJ_ScheduleDetailsEntity dm = new WJ_ScheduleDetailsEntity(); dm.ActionDescribe = "執行完成:" + result.Message ; dm.ActionStep = (int)ADJ.Job.Entity.EnumType.JobStep.執行完成; dm.CreateTime = DateTime.Now; dm.JobGroup = context.JobDetail.Key.Group; dm.JobName = context.JobDetail.Key.Name; dm.IsSuccess = 1; new WJ_ScheduleManage().AddScheduleDetails(dm); #endregion } else { #region 加入執行明細 WJ_ScheduleDetailsEntity dm = new WJ_ScheduleDetailsEntity(); dm.ActionDescribe = "執行任務計劃中,執行計劃過程出錯."+result.Message; dm.ActionStep = (int)ADJ.Job.Entity.EnumType.JobStep.執行任務計劃中; dm.CreateTime = DateTime.Now; dm.JobGroup = context.JobDetail.Key.Group; dm.JobName = context.JobDetail.Key.Name; dm.IsSuccess = 0; new WJ_ScheduleManage().AddScheduleDetails(dm); #endregion } new WJ_ScheduleManage().UpdateScheduleNextTime(new WJ_ScheduleEntity() { JobGroup = context.JobDetail.Key.Group, JobName = context.JobDetail.Key.Name, NextTime = context.NextFireTimeUtc.Value.DateTime.AddHours(8) }); } catch (Exception ex) { #region 加入執行明細 WJ_ScheduleDetailsEntity dm = new WJ_ScheduleDetailsEntity(); dm.ActionDescribe = "執行任務計劃中,執行計劃過程出錯:" + ex.Message + "/r/n" + ex.StackTrace; dm.ActionStep = (int)ADJ.Job.Entity.EnumType.JobStep.執行任務計劃中; dm.CreateTime = DateTime.Now; dm.JobGroup = context.JobDetail.Key.Group; dm.JobName = context.JobDetail.Key.Name; dm.IsSuccess = 0; new WJ_ScheduleManage().AddScheduleDetails(dm); #endregion DoApplication.WriteLogFile(ex.Message + "\r\n" + ex.StackTrace); } }); } }
note:這裡執行的Execute方法引數IJobExecutionContext 中,會自動把作業排程的詳細資訊帶過來,作業名稱,作業組名,作業下次執行時間,作業執行時間等等,這裡的內容也是至關重要的,比如根據作業組,作業名稱我們可以從資料庫找到相應的作業排程詳細,更新運算元據庫。
2、針對某個作業計劃進行停止,啟動。
/// <summary> /// 暫停指定任務計劃 /// </summary> /// <returns></returns> public JsonResult StopScheduleJob(string jobGroup, string jobName) { try { scheduler = GetScheduler(); scheduler.PauseJob(new JobKey(jobName, jobGroup)); new WJ_ScheduleManage().UpdateScheduleStatus(new WJ_ScheduleEntity() { JobName = jobName, JobGroup = jobGroup, Status = (int)ADJ.Job.Entity.EnumType.JobStatus.已停止 }); return Json(new StatusView() { Status = 0, Msg = "停止任務計劃成功!" }, JsonRequestBehavior.AllowGet); } catch (Exception ex) { DoApplication.WriteLogFile(ex.Message + "/r/n" + ex.StackTrace); return Json(new StatusView() { Status = -1, Msg = "停止任務將計劃失敗!" }, JsonRequestBehavior.AllowGet); } } /// <summary> /// 開啟指定的任務計劃 /// </summary> /// <returns></returns> public JsonResult RunScheduleJob(string jobGroup, string jobName) { try { var sm = new WJ_ScheduleManage().GetScheduleModel(new WJ_ScheduleEntity() { JobName = jobName, JobGroup = jobGroup }); AddScheduleJob(sm); sm.Status = (int)ADJ.Job.Entity.EnumType.JobStatus.已啟用; new WJ_ScheduleManage().UpdateScheduleStatus(sm); scheduler = GetScheduler(); scheduler.ResumeJob(new JobKey(jobName, jobGroup)); return Json(new StatusView() { Status = 0, Msg = "啟動成功!" }, JsonRequestBehavior.AllowGet); } catch (Exception ex) { DoApplication.WriteLogFile(ex.Message + "/r/n" + ex.StackTrace); return Json(new StatusView() { Status = -1, Msg = "啟動失敗!" }, JsonRequestBehavior.AllowGet); } }
最後注意:
1、這個專案完全使用啦cron-like表示式實現觸發器配置,如果你對cron不瞭解,那麼我上篇中有針對cron做介紹講解,如果你對cron瞭解而沒有一個合適的生成工具,那麼入左上方2個群,找我。
2、這個專案部署在IIS中,那麼就要設定應用程式池的回收機制為,永不回收,配置下,如果不會配置,入左上方2個群,找我。