好比說,財務系統須要在每月初生成上一個月的財務報表。html
好比說,天天或每週固定時間對數據庫更新。git
好比說,天天定時發送郵件。github
這些須要在某個預約的時間點週期性的執行某個特定的任務的功能(也就是任務調度),可使用任務調度框架——Quartz .NET數據庫
Quartz.NET是一個開源的任務調度框架(做業調度框架),是從Java移植過來的,使用較爲普遍!express
調度器(Scheduler):存放觸發器和定時任務,根據觸發器執行定時任務框架
觸發器(Trigger):決定執行時間,執行間隔,運行次數,故觸發器用來告訴調度程序做業何時觸發異步
任務(Job):須要定時或是週期性執行的任務async
使用流程:ui
建立調度器-->建立任務-->建立觸發器-->Job和Trigger註冊到調度器-->啓動調度器;.net
接口/類 | 做用 |
---|---|
IScheduler | 調度器接口 |
IJob | 任務接口,將須要定時執行的方法實如今該接口的Excute方法中 |
IJobDetail | 用於定義Job的實例 |
ITrigger | 觸發器接口 |
JobBuilder | 任務構造者:用於建立任務實例 |
TriggerBuilder | 觸發器構造者:用於建立觸發器實例 |
JobDetailImpl | 實現了IJobDetail類 |
JobKey | 任務名 |
TriggerKey | 觸發器名 |
其中觸發器的類型
觸發器最經常使用的主要有兩種:
SimpleTrigger:用於指定任務重複執行的時間間隔
IMutableTrigger:用於指定任務重複執行的具體時間
①安裝Quartz程序包
當前時間:2020年3月18日 23:20:59,最新版本的Quartz.NET爲3.0.7
每次的版本的變化,API變化都好大,因此在這裏註明當前的使用版本!
建議使用最新版本,新版本都是異步方法實現的。
NuGet:Install-Package Quartz -Version 3.0.7
②新建TestJob.cs
實現IJob接口
public class TestJob : IJob { public async Task Execute(IJobExecutionContext context) { await Task.Run(() => Console.WriteLine($"{DateTime.Now}:執行任務了……")); } }
//間隔5s重複一次執行指定的任務 public static async void WithInterval() { //-------1.準備調度者 ISchedulerFactory factory = new StdSchedulerFactory(); //建立調度器工廠 IScheduler scheduler = await factory.GetScheduler(); //建立調度者 //-------2.準備任務 JobBuilder jobBuilder = JobBuilder.Create<TestJob>();//建立任務構造者:JobBuilder IJobDetail job1 = jobBuilder.Build();//建立任務 //-------3.準備觸發器 TriggerBuilder triggerBuilder = TriggerBuilder.Create() .StartNow()//當即生效 .WithSimpleSchedule(x=>x.WithIntervalInSeconds(5) .RepeatForever()); //建立觸發器構造者:TriggerBuilder ISimpleTrigger trigger = triggerBuilder.WithIdentity("trigger1","group2").Build() as ISimpleTrigger;//建立觸發器 //-------4.將任務與觸發器添加到調度器中 await scheduler.ScheduleJob(job1, trigger); await scheduler.Start();//開始執行 }
【代碼說明】
示例中使用的而是ISimpleTrigger
類型的觸發器,能夠精準的設置任務重複的時間間隔。
其中的觸發器構造者中的
triggerBuilder.StartNow()
表示觸發器當即生效
triggerBuilder.StartAt(DateTimeOffset startTimeUtc)
設置觸發器生效的時間
triggerBuilder.EndAt(DateTimeOffset startTimeUtc)
設置觸發器失效的時間
其中使用WithIntervalInSeconds(5)
表示每五秒觸發一次任務
其餘的一些按照小時和天的作間隔,以及明確觸發次數的方法都簡單明確,根據VS的智能提示便可瞭解,不一一列舉於此!
示例中使用RepeatForever()
表示重複無窮次,仍是可使用WithRepeatCount()
設置重複的次數的。
這裏有一個細節問題,好比說,設置執行三次,WithRepeatCount(3)
,可是注意實際會執行4次
Quartz.NET的接口比較繁多,第一個示例中是使用的最基礎的方法,下面代碼示例將換一種簡寫的方式。
//天天按照指定的時間點執行任務 public static async void AtHourAndMinute() { //建立調度器 IScheduler scheduler = await new StdSchedulerFactory().GetScheduler(); //建立任務 //JobDetailImpl job1 = new JobDetailImpl("TestJob1", "group1", typeo(TestJob))//JobDetailImpl是IJobDetail的實現類 //等價於: IJobDetail job1 = JobBuilder.Create<TestJob>().WithIdentity("Testjob1""group1").Build(); //建立觸發器 IMutableTrigger trigger2job1 = CronScheduleBuilder.DailyAtHourAndMinut(03, 50).Build();//天天更具某時間點重複觸發任務 //將任務和觸發器添加到調度器中 trigger2job1.Key = new TriggerKey("trigger1");//注意必定要給觸發器命名 await scheduler.ScheduleJob(job1, trigger2job1); //開始執行調度者 await scheduler.Start(); }
【代碼說明】
示例中使用的是IMutableTrigger
類型的觸發器
經過CronScheduleBuilder
類的靜態方法能夠設置觸發的具體的某一日
設置觸發時間爲天天的某時某分:DailyAtHourAndMinut(03, 50)
設置觸發時間是一週中的哪幾天中的幾時幾分 :AtHourAndMinuteOnGivenDaysOfWeek(int hour , int min, params DayOfWeek[] daysOfWeek)
設置觸發時間是每個月中某天某時某分 :CronScheduleBuilder.MonthlyOnDayAndHourAndMinute(int dayOfMonth, int hour, int min).Build()
封裝好的一些方法仍是有必定侷限的(可是我本身夠用的了),關於其餘的一些複雜的週期任務,都是可使用cron expression,使用cron expression能夠定義你能想到的全部觸發時間和週期
cron expression什麼樣?怎麼用?例如設置觸發的時間是:每一年每個月的2點18分40秒CronScheduleBuilder.CronSchedule("40 18 2 ? * * *").WithIdentity("trigger1").Build();
關於cron expression寫起來仍是有點麻煩的,可使用一些在線生成器爲咱們自動的生成指望的表達式。
前面的示例爲了簡潔的表示Quartz.NET的一些API的使用,
項目中都是把爲定時任務,整個的操做流程封裝在一個靜態方法中,存放在咱們自定義的Job類中
作一個簡單的示例:定時發送短信。
自定義Job,實現IJob接口,同時把建立調度器對象,建立觸發器和任務封裝於其中,做爲一個靜態方法
class TestJob2 : IJob { public async Task Execute(IJobExecutionContext context) { try { JobDataMap dataMap = context.MergedJobDataMap; string tag = dataMap.GetString("tag"); string title = dataMap.GetString("title"); string content = dataMap.GetString("content"); string description = dataMap.GetString("description"); string tels = dataMap.GetString("tels"); //執行定時任務:模擬發送短信 await Task.Run(() => Console.WriteLine($"發短信:【{tag}】,{title}:{content },{description},電話:{tels}。")); //await context.Scheduler.Shutdown();//表示完成當前的定時任務,關閉調度器 //記入日誌 Console.WriteLine("執行了一次定時任務,記入日誌"); } catch (Exception ex) { //記入日誌Log.Error() Console.WriteLine(ex.Message); } } //將建立定時任務的全部操做封裝在此 public static async void SendMessage(string starttime, string cronStr,string tag, string title, string content,string description, string tels) { try { //建立調度器 IScheduler scheduler = await new StdSchedulerFactory().GetScheduler(); //爲任務準備參數 DateTime time = DateTime.Parse(starttime); JobDataMap jobData = new JobDataMap() { new KeyValuePair<string, object>("tag", tag), new KeyValuePair<string, object>("title", title), new KeyValuePair<string, object>("content", content), new KeyValuePair<string, object>("description", description), new KeyValuePair<string, object>("tels", tels), }; //建立任務: //注意能夠用時間作組名:DateTime.Now.ToLongDateString() IJobDetail job = JobBuilder.Create<TestJob2>() .WithIdentity("Testjob1", "group1") .SetJobData(jobData) .Build(); //建立觸發器 ITrigger trigger = TriggerBuilder.Create() .WithIdentity("triger1", "group1") .StartAt(time)//觸發器開始時間//.StartNow()如今開始 .WithCronSchedule(cronstr) .Build(); //將任務和觸發器添加到調度器中 await scheduler.ScheduleJob(job, trigger); await scheduler.Start(); } catch (Exception ex) { //記入日誌 Console.WriteLine(ex.Message); } } }
調用:
public static async void PackageJob() { //從系統當前時間,每隔5s,發送一條短信:【新聞】,新冠病毒,治癒者愈來愈多,普天同慶,10086。 await Task.Run(() => TestJob2.SendMessage(DateTime.Now .ToString(),"/5 * * ? * *","新聞", "新冠病毒", "治癒者愈來愈多", "普天同慶", "10086")); }
【代碼說明】
使用JobDataMap
類型存放須要傳遞到IJob
接口的Excute(IJobExecutionContext context)
方法中
在JobDataMap
中以鍵值對的方式存放數據,jobDataMao.Add("key",value)
在定義Job的時候,使用觸發器對象中的方法jobBuilder.SetJobData(jobData)
將JobDataMap類型的數據傳遞到任務中
使用JobDataMap dataMap = context.MergedJobDataMap;
獲取傳遞到Excute()中的JobDataMap類型的數據
使用string value = dataMap.GetString("key");
獲取數據
由於定時任務的是延時的執行的,因此切記必定要把每一個週期中執行的定時任務記入到日誌中,便於維護管理!
注意,由於實現了IJob接口的任務類,其Excute()方法是在一個單獨的線程中運行的,因此其異常的處理也在Excute()中使用try……catch……進行處理
BTW:在MVC項目中使用Quartz .NET,直接在Global.asax.cs中的Application_Start()運行封裝好的定時任務便可
注意:使用Quartz.NET中的Job,是沒法實現任何關於Web的相關操做
一個調度器中能夠調度多個方法
使用 scheduler.ScheduleJob(job,trigger)
將指定的任務和觸發器添加到指定的調度器中,能夠屢次添加,從而實現一個調度器中調度多個任務
可是有一點要注意:一個任務能夠有多個觸發器,可是一個觸發器只能對應一個任務
調度器能夠添加任務,那麼就必定是能夠移除任務的
//中止觸發器 await scheduler.PauseTrigger(triggerKey); //移除觸發器 await scheduler.UnscheduleJob(triggerKey); //刪除任務 await scheduler.DeleteJob(jobkey);
調度器能夠開始運行,那麼就必定中止運行:
context.Scheduler.Shutdown();
表示完成當前的定時任務,關閉調度器
Undone……
可參考監聽器:JobListeners/TriggerListeners/SchedulerListeners
監聽器:Quartz.NET使用教程
Quartz(Java)源碼:Quartz 源碼解析
遠程管理及可視化操做: ASP.NET MVC5 實現基於Quartz.NET任務調度
配置方式1:Quartz.NET文檔 入門教程
配置方式2:Quartz.Net定時任務簡單實用(實例)
Cron Expression:Cron Expression Generator
封裝任務和傳參:NET做業調度(定時任務)-Quartz.Net