之前寫過一篇文件《DotNetCore跨平臺~Quartz熱部署的福音~監控文件夾的變化》,今天主要把框架優化了一下,支持外部觸發,並支持外部將參數以JobDataMap形式進行輸入,然後在咱們的Job里進行使用它,故稱參數化任務。 Quartz使用場景: 今天說的外部觸發的任務是指第一種,即在未來 ...
之前寫過一篇文件《DotNetCore跨平臺~Quartz熱部署的福音~監控文件夾的變化》,今天主要把框架優化了一下,支持外部觸發,並支持外部將參數以JobDataMap形式進行輸入,然後在咱們的Job里進行使用它,故稱參數化任務。
Quartz使用場景:
- 定時單次任務:在未來某個時間去執行一次
- 定點任務 :在某個時間去執行,可以是輪詢的
- 周期任務 :按某個時間間隔去輪詢執行
今天說的外部觸發的任務是指第一種,即在未來某個時間點去執行,並且只執行一次。說一下思路,這種任務某個JobBase的子類,它需要重寫屬性IsSingle,將值設為1表示單次任務,然後在Quartz啟動後,它會被立即執行,執行完成後,銷毀!
用例:你可以在quartz調度中心裡對外公開一些方法,讓你的Job依賴於某個時間點和參數去執行,執行一次就停止,這樣我們的調度就更加靈活了。
為單次任務添加了IsSingle屬性
[DisallowConcurrentExecution()] public abstract class JobBase : ISchedulingJob { #region Properties /// <summary> /// 取消資源 /// </summary> public CancellationTokenSource CancellationSource => new CancellationTokenSource(); /// <summary> /// 執行計劃,除了立即執行的JOB之後,其它JOB需要實現它 /// </summary> public virtual string Cron => "* * * * * ?"; /// <summary> /// 是否為單次任務,黑為false /// </summary> public virtual bool IsSingle => false; /// <summary> /// Job的名稱,預設為當前類名 /// </summary> public virtual string JobName => GetType().Name; /// <summary> /// Job執行的超時時間(毫秒),預設5分鐘 /// </summary> public virtual int JobTimeout => 5 * 60 * 1000; #endregion Properties #region Methods /// <summary> /// Job具體類去實現自己的邏輯 /// </summary> protected abstract void ExcuteJob(IJobExecutionContext context, CancellationTokenSource cancellationSource); /// <summary> /// 當某個job超時時,它將被觸發,可以發一些通知郵件等 /// </summary> /// <param name="arg"></param> private void CancelOperation(object arg) { CancellationSource.Cancel(); StdSchedulerFactory.GetDefaultScheduler().Result.Interrupt(new JobKey(JobName)); Console.WriteLine(JobName + "Job執行超時,已經取消,等待下次調度..."); } #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開始執行", 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 }
統一的加入Job隊列的方法
在我們之前的QuartzManager管理者中,我們需要添加對單次任務的支持,這點我們將任務加入到quartz的代碼進行了重構,提取到了方法里。
/// <summary> /// 將類型添加到Job隊列 /// </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); } }
對外公開的參數化介面
而對於外界如果希望再次觸發這個單次任務,我們可以在QuartzManager里公開一個方法,用來向當前SchedulerFactory里添加新的Job就可以了,這個方法很簡單,可以提供一個預設的時間策略,如預設為1分鐘後執行,也可以自己控制時間。
/// <summary> /// 任務在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> /// 任務在某個時間之後被執行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); }
那麼,現在某個任務調度中心就更加完善了,開發人員在使用時也很簡單,只要繼承JobBase,或者去實現ISchedulingJob介面就可以了,非常靈活!
感謝各位的閱讀!
quartz,dotnet core我們還在繼續研究的路上!