目錄
- 0. 任務排程
- 1. Quartz .NET
- 1.1 基本概念
- 1.2 主要接口和對象
- 2. 使用示例
- 2.0 準備工作
- 2.1 每間隔一定時間間隔執行一次任務
- 2.3 某天的固定時間點執行任務
- 2.4 封裝整個定時任務,并給任務傳遞參數
- 2.5 關于排程器的一些說明
- 2.6 關于監聽器
- 參考及示例代碼下載下傳
shanzm-2020年3月25日 21:28:09
比如說,财務系統需要在每個月初生成上一個月的财務報表。
比如說,每天或每周固定時間對資料庫更新。
比如說,每天定時發送郵件。
這些需要在某個預定的時間點周期性的執行某個特定的任務的功能(也就是任務排程),可以使用任務排程架構——Quartz .NET
Quartz.NET是一個開源的任務排程架構(作業排程架構),是從Java移植過來的,使用較為廣泛!
排程器(Scheduler):存放觸發器和定時任務,根據觸發器執行定時任務
觸發器(Trigger):決定執行時間,執行間隔,運作次數,故觸發器用來告訴排程程式作業什麼時候觸發
任務(Job):需要定時或是周期性執行的任務
使用流程:
建立排程器-->建立任務-->建立觸發器-->Job和Trigger注冊到排程器-->啟動排程器;
接口/類 | 作用 |
---|---|
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()
設定重複的次數的。
這裡有一個細節問題,比如說,設定執行三次,
,但是注意實際會執行4次WithRepeatCount(3)
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寫起來還是有點麻煩的,可以使用一些線上生成器為我們自動的生成期望的表達式。
推薦:Cron Expression Generator
前面的示例為了簡潔的表示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的時候,使用觸發器對象中的方法
将JobDataMap類型的資料傳遞到任務中jobBuilder.SetJobData(jobData)
-
擷取傳遞到Excute()中的JobDataMap類型的資料JobDataMap dataMap = context.MergedJobDataMap;
擷取資料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 3.x Tutorial
- 監聽器:Quartz.NET使用教程
- Quartz(Java)源碼:Quartz 源碼解析
- 遠端管理及可視化操作: ASP.NET MVC5 實作基于Quartz.NET任務排程
- 配置方式1:Quartz.NET文檔 入門教程
- 配置方式2:Quartz.Net定時任務簡單實用(執行個體)
- Cron Expression:Cron Expression Generator
- 封裝任務和傳參:NET作業排程(定時任務)-Quartz.Net
- 取消任務:Net作業排程-----Quartz.Net
作者:shanzm
歡迎交流,歡迎指教!