之前写过一篇文件《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我们还在继续研究的路上!