關於Quartz .NET(V3.0.7)的簡要說明

  • 2020 年 3 月 26 日
  • 筆記

shanzm-2020年3月25日 21:28:09

0. 任務調度

比如說,財務系統需要在每個月初生成上一個月的財務報表。

比如說,每天或每周固定時間對數據庫更新。

比如說,每天定時發送郵件。

這些需要在某個預定的時間點周期性的執行某個特定的任務的功能(也就是任務調度),可以使用任務調度框架——Quartz .NET

Quartz.NET是一個開源的任務調度框架(作業調度框架),是從Java移植過來的,使用較為廣泛!



1. Quartz .NET

1.1 基本概念

調度器(Scheduler):存放觸發器和定時任務,根據觸發器執行定時任務

觸發器(Trigger):決定執行時間,執行間隔,運行次數,故觸發器用來告訴調度程序作業什麼時候觸發

任務(Job):需要定時或是周期性執行的任務

使用流程:

創建調度器–>創建任務–>創建觸發器–>Job和Trigger註冊到調度器–>啟動調度器;

1.2 主要接口和對象

接口/類 作用
IScheduler 調度器接口
IJob 任務接口,將需要定時執行的方法實現在該接口的Excute方法中
IJobDetail 用於定義Job的實例
ITrigger 觸發器接口
JobBuilder 任務構造者:用於創建任務實例
TriggerBuilder 觸發器構造者:用於創建觸發器實例
JobDetailImpl 實現了IJobDetail類
JobKey 任務名
TriggerKey 觸發器名

其中觸發器的類型

觸發器最常用的主要有兩種:

SimpleTrigger:用於指定任務重複執行的時間間隔

IMutableTrigger:用於指定任務重複執行的具體時間



2. 使用示例

2.0 準備工作

①安裝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}:執行任務了……"));      }  }  

2.1 每間隔一定時間間隔執行一次任務

//間隔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次

2.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

2.4 封裝整個定時任務,並給任務傳遞參數

前面的示例為了簡潔的表示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()運行封裝好的定時任務即可

2.5 關於調度器的一些說明

  • 一個調度器中可以調度多個方法

    使用 scheduler.ScheduleJob(job,trigger)將指定的任務和觸發器添加到指定的調度器中,可以多次添加,從而實現一個調度器中調度多個任務

    但是有一點要注意:一個任務可以有多個觸發器,但是一個觸發器只能對應一個任務

  • 調度器可以添加任務,那麼就一定是可以移除任務的

    //停止觸發器  await scheduler.PauseTrigger(triggerKey);  //移除觸發器  await scheduler.UnscheduleJob(triggerKey);  //刪除任務  await scheduler.DeleteJob(jobkey);  
  • 調度器可以開始運行,那麼就一定停止運行:

    context.Scheduler.Shutdown();表示完成當前的定時任務,關閉調度器

2.6 關於監視器

Undone……

可參考監聽器:JobListeners/TriggerListeners/SchedulerListeners



參考及示例代碼下載