DotNetCore跨(kua)平(ping)臺~Quartz定時單次任務
之前寫過一篇文件《DotNetCore跨平臺~Quartz熱部署的福音~監控文件夾的變化》,今天主要把框架(jia)優化(hua)了一(yi)下,支持外部觸發(fa),并支持外部將參數以JobDataMap形式進(jin)行(xing)輸入(ru),然后(hou)在(zai)咱(zan)們的Job里(li)進(jin)行(xing)使用它,故稱參數化(hua)任(ren)務(wu)。
Quartz使用場景:
- 定時單次任務:在未來某個時間去執行一次
- 定點任務 :在某個時間去執行,可以是輪詢的
- 周期任務 :按某個時間間隔去輪詢執行
今天說的外部觸(chu)發的任務(wu)是指第一(yi)種,即(ji)(ji)在未來某個時間點(dian)去執(zhi)行(xing),并且只執(zhi)行(xing)一(yi)次(ci)。說一(yi)下思路,這種任務(wu)某個JobBase的子類,它(ta)需(xu)要重寫屬性IsSingle,將值設為1表示(shi)單次(ci)任務(wu),然后在Quartz啟動后,它(ta)會被立即(ji)(ji)執(zhi)行(xing),執(zhi)行(xing)完(wan)成后,銷毀!
用例:你可以在quartz調度(du)(du)中心里對外公開(kai)一(yi)些方法,讓你的(de)(de)Job依賴于某個時間點和(he)參數去執(zhi)行,執(zhi)行一(yi)次就停(ting)止,這樣(yang)我們的(de)(de)調度(du)(du)就更加靈活了。
為單次任務添加了IsSingle屬性(xing)
[DisallowConcurrentExecution()] public abstract class JobBase : ISchedulingJob { #region Properties /// <summary> /// 取消資(zi)源 /// </summary> public CancellationTokenSource CancellationSource => new CancellationTokenSource(); /// <summary> /// 執(zhi)行計劃,除了立即執(zhi)行的JOB之(zhi)后,其它(ta)JOB需要實現它(ta) /// </summary> public virtual string Cron => "* * * * * ?"; /// <summary> /// 是否為(wei)單次任務,黑為(wei)false /// </summary> public virtual bool IsSingle => false; /// <summary> /// Job的名稱,默認(ren)為當(dang)前類(lei)名 /// </summary> public virtual string JobName => GetType().Name; /// <summary> /// Job執行的超時時間(毫秒),默認5分鐘 /// </summary> public virtual int JobTimeout => 5 * 60 * 1000; #endregion Properties #region Methods /// <summary> /// Job具體類(lei)去(qu)實現自己的邏輯 /// </summary> protected abstract void ExcuteJob(IJobExecutionContext context, CancellationTokenSource cancellationSource); /// <summary> /// 當(dang)某(mou)個job超時時,它將被觸發,可以發一(yi)些(xie)通知(zhi)郵件等 /// </summary> /// <param name="arg"></param> private void CancelOperation(object arg) { CancellationSource.Cancel(); StdSchedulerFactory.GetDefaultScheduler().Result.Interrupt(new JobKey(JobName)); Console.WriteLine(JobName + "Job執行超(chao)時(shi),已(yi)經取消,等(deng)待下次調度(du)..."); } #endregion Methods #region IJob 成員 public Task Execute(IJobExecutionContext context) { Timer timer = null; try { timer = new Timer(CancelOperation, null, JobTimeout, Timeout.Infinite); Console.WriteLine(DateTime.Now.ToString() + "{0}這個Job開始(shi)執行", context.JobDetail.Key.Name); if (context.JobDetail.JobDataMap != null) { foreach (var pa in context.JobDetail.JobDataMap) Console.WriteLine($"JobDataMap,key:{pa.Key},value:{pa.Value}"); } ExcuteJob(context, CancellationSource); } catch (Exception ex) { Console.WriteLine(this.GetType().Name + "error:" + ex.Message); } finally { if (timer != null) timer.Dispose(); } return Task.CompletedTask; } #endregion }
統一的(de)加入Job隊列的(de)方法
在我(wo)們(men)之前的(de)(de)QuartzManager管理者中(zhong),我(wo)們(men)需要添加對單(dan)次任務(wu)的(de)(de)支(zhi)持(chi),這點我(wo)們(men)將任務(wu)加入到(dao)quartz的(de)(de)代碼進行(xing)了重(zhong)構(gou),提取(qu)到(dao)了方法里。
/// <summary> /// 將類型添加到Job隊(dui)列 /// </summary> /// <param name="type">類型</param> /// <param name="dt">時間點</param> /// <param name="param">參數</param> private static void JoinToQuartz(Type type, DateTimeOffset dt, Dictionary<string, object> param = null) { var obj = Activator.CreateInstance(type); if (obj is ISchedulingJob) { var tmp = obj as ISchedulingJob; string cron = tmp.Cron; string name = tmp.JobName; var cancel = tmp.CancellationSource; var jobDetail = JobBuilder.Create(type) .WithIdentity(name) .Build(); if (param != null) foreach (var dic in param) jobDetail.JobDataMap.Add(dic.Key, dic.Value); ITrigger jobTrigger; if (tmp.IsSingle) { jobTrigger = TriggerBuilder.Create() .WithIdentity(name + "Trigger") .StartAt(dt) .Build(); } else { jobTrigger = TriggerBuilder.Create() .WithIdentity(name + "Trigger") .StartNow() .WithCronSchedule(cron) .Build(); } StdSchedulerFactory.GetDefaultScheduler().Result.ScheduleJob(jobDetail, jobTrigger, cancel.Token); LoggerInfo($"->任務模塊{name}被裝載...", ConsoleColor.Yellow); } }
對(dui)外公開的(de)參數化接口(kou)
而對于外界如果希望再次觸發這個單次任務,我們可以在QuartzManager里公開一個方法,用來向當前SchedulerFactory里添加新的(de)Job就可(ke)以了,這個(ge)方(fang)法很簡(jian)單,可(ke)以提供一個(ge)默認(ren)的(de)時間策略(lve),如默認(ren)為1分鐘(zhong)后執行,也可(ke)以自己(ji)控制時間。
/// <summary> /// 任(ren)務在(zai)1分鐘之后被執行1次 /// </summary> /// <param name="type"></param> /// <param name="job"></param> /// <param name="param"></param> public static void SignalJob(Type type, Dictionary<string, object> param) { SignalJob(type, DateTimeOffset.Now.AddSeconds(10), param); } /// <summary> /// 任(ren)務在某個時間(jian)之后被(bei)執(zhi)行1次 /// </summary> /// <param name="type"></param> /// <param name="job"></param> /// <param name="offset"></param> /// <param name="param"></param> public static void SignalJob(Type type, DateTimeOffset offset, Dictionary<string, object> param) { JoinToQuartz(type, offset); }
那么,現在某(mou)個任務調度中心就更加完善了,開發人員在使(shi)用時也很簡(jian)單,只要(yao)繼承JobBase,或者去實(shi)現ISchedulingJob接口就可以了,非(fei)常靈活!
感謝各位的閱讀!
quartz,dotnet core我(wo)們還(huan)在(zai)繼續(xu)研(yan)究的路上!