扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
这篇文章给大家介绍如何使用DotNet任务调度组件Quartz.NET,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
成都创新互联主营万宁网站建设的网络公司,主营网站建设方案,成都APP应用开发,万宁h5微信小程序搭建,万宁网站营销推广欢迎万宁等地区企业咨询很多的软件项目中都会使用到定时任务、定时轮询数据库同步,定时邮件通知等功能。.NET Framework具有“内置”定时器功能,通过System.Timers.Timer类。在使用Timer类需要面对的问题:计时器没有持久化机制;计时器具有不灵活的计划(仅能设置开始时间和重复间隔,没有基于日期,时间等);计时器不使用线程池(每个定时器一个线程);计时器没有真正的管理方案 - 你必须编写自己的机制,以便能够记住,组织和检索任务的名称等。
如果需要在.NET实现定时器的功能,可以尝试使用以下这款开源免费的组件Quartz.Net组件。目前Quartz.NET版本为3.0,修改了原来的一些问题:修复由于线程本地存储而不能与AdoJobStore协同工作的调度器信令;线程局部状态完全删除;quartz.serializer.type是必需的,即使非序列化RAMJobStore正在使用;JSON序列化错误地称为序列化回调。
一.Quart.NET概述:
Quartz是一个作业调度系统,可以与任何其他软件系统集成或一起使用。作业调度程序是一个系统,负责在执行预处理程序时执行(或通知)其他软件组件 - 确定(调度)时间到达。Quartz是非常灵活的,并且包含多个使用范例,可以单独使用或一起使用,以实现您所需的行为,并使您能够以您的项目看起来最“自然”的方式编写代码。组件的使用非常轻便,并且需要非常少的设置/配置 - 如果您的需求相对基础,它实际上可以使用“开箱即用”。Quartz是容错的,并且可以在系统重新启动之间保留(记住)您的预定作业。尽管Quartz对于在给定的时间表上简单地运行某些系统进程非常有用,但当您学习如何使用Quartz来驱动应用程序的业务流程时,Quartz的全部潜能可以实现。
Quartz是作为一个小的动态链接库(.dll文件)分发的,它包含所有的核心Quartz功能。 此功能的主要接口(API)是调度程序接口。 它提供简单的操作,如调度/非调度作业,启动/停止/暂停调度程序。如果你想安排你自己的软件组件执行,他们必须实现简单的Job接口,它包含方法execute()。 如果希望在计划的触发时间到达时通知组件,则组件应实现TriggerListener或JobListener接口。主要的Quartz'进程'可以在您自己的应用程序或独立应用程序(使用远程接口)中启动和运行。
二.Quartz.NET主体类和方法解析:
1.StdSchedulerFactory类:创建QuartzScheduler实例。
////// 返回此工厂生成的调度程序的句柄。 /// //////如果 public virtual IScheduler GetScheduler() { if (cfg == null) { Initialize(); } SchedulerRepository schedRep = SchedulerRepository.Instance; IScheduler sched = schedRep.Lookup(SchedulerName); if (sched != null) { if (sched.IsShutdown) { schedRep.Remove(SchedulerName); } else { return sched; } } sched = Instantiate(); return sched; }方法之一没有先前调用,然后是默认(no-arg) 方法将被这个方法调用。 ///
public interface ISchedulerFactory { ////// Returns handles to all known Schedulers (made by any SchedulerFactory /// within this app domain.). /// ICollectionAllSchedulers { get; } /// /// Returns a client-usable handle to a IScheduler GetScheduler(); ///. /// /// Returns a handle to the Scheduler with the given name, if it exists. /// IScheduler GetScheduler(string schedName); }
2.JobDetailImpl:传递给定作业实例的详细信息属性。
////// 获取或设置与 public virtual JobDataMap JobDataMap { get { if (jobDataMap == null) { jobDataMap = new JobDataMap(); } return jobDataMap; } set { jobDataMap = value; } }相关联的 。 ///
3.JobKey:键由名称和组组成,名称必须是的,在组内。 如果只指定一个组,则默认组将使用名称。
[Serializable] public sealed class JobKey : Key{ public JobKey(string name) : base(name, null) { } public JobKey(string name, string group) : base(name, group) { } public static JobKey Create(string name) { return new JobKey(name, null); } public static JobKey Create(string name, string group) { return new JobKey(name, group); } }
4.StdSchedulerFactory.Initialize():
////// 使用初始化 public virtual void Initialize(NameValueCollection props) { cfg = new PropertiesParser(props); ValidateConfiguration(); } protected virtual void ValidateConfiguration() { if (!cfg.GetBooleanProperty(PropertyCheckConfiguration, true)) { // should not validate return; } // determine currently supported configuration keys via reflection List///给定键值集合对象的内容。 /// supportedKeys = new List (); List fields = new List (GetType().GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy)); // choose constant string fields fields = fields.FindAll(field => field.FieldType == typeof (string)); // read value from each field foreach (FieldInfo field in fields) { string value = (string) field.GetValue(null); if (value != null && value.StartsWith(ConfigurationKeyPrefix) && value != ConfigurationKeyPrefix) { supportedKeys.Add(value); } } // now check against allowed foreach (string configurationKey in cfg.UnderlyingProperties.AllKeys) { if (!configurationKey.StartsWith(ConfigurationKeyPrefix) || configurationKey.StartsWith(ConfigurationKeyPrefixServer)) { // don't bother if truly unknown property continue; } bool isMatch = false; foreach (string supportedKey in supportedKeys) { if (configurationKey.StartsWith(supportedKey, StringComparison.InvariantCulture)) { isMatch = true; break; } } if (!isMatch) { throw new SchedulerConfigException("Unknown configuration property '" + configurationKey + "'"); } } }
三.Quartz.NET的基本应用:
下面提供一些较为通用的任务处理代码:
1.任务处理帮助类:
////// 任务处理帮助类 /// public class QuartzHelper { public QuartzHelper() { } public QuartzHelper(string quartzServer, string quartzPort) { Server = quartzServer; Port = quartzPort; } ////// 锁对象 /// private static readonly object Obj = new object(); ////// 方案 /// private const string Scheme = "tcp"; ////// 服务器的地址 /// public static string Server { get; set; } ////// 服务器的端口 /// public static string Port { get; set; } ////// 缓存任务所在程序集信息 /// private static readonly DictionaryAssemblyDict = new Dictionary (); /// /// 程序调度 /// private static IScheduler _scheduler; ////// 初始化任务调度对象 /// public static void InitScheduler() { try { lock (Obj) { if (_scheduler != null) return; //配置文件的方式,配置quartz实例 ISchedulerFactory schedulerFactory = new StdSchedulerFactory(); _scheduler = schedulerFactory.GetScheduler(); } } catch (Exception ex) { throw new Exception(ex.Message); } } ////// 启用任务调度 /// 启动调度时会把任务表中状态为“执行中”的任务加入到任务调度队列中 /// public static void StartScheduler() { try { if (_scheduler.IsStarted) return; //添加全局监听 _scheduler.ListenerManager.AddTriggerListener(new CustomTriggerListener(), GroupMatcher.AnyGroup()); _scheduler.Start(); //获取所有执行中的任务 List listTask = TaskHelper.GetAllTaskList().ToList(); if (listTask.Count > 0) { foreach (TaskModel taskUtil in listTask) { try { ScheduleJob(taskUtil); } catch (Exception e) { throw new Exception(taskUtil.TaskName,e); } } } } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 启用任务 /// 任务信息 /// 是否删除原有任务 /// public static void ScheduleJob(TaskModel task, bool isDeleteOldTask = false) { if (isDeleteOldTask) { //先删除现有已存在任务 DeleteJob(task.TaskID.ToString()); } //验证是否正确的Cron表达式 if (ValidExpression(task.CronExpressionString)) { IJobDetail job = new JobDetailImpl(task.TaskID.ToString(), GetClassInfo(task.AssemblyName, task.ClassName)); //添加任务执行参数 job.JobDataMap.Add("TaskParam", task.TaskParam); CronTriggerImpl trigger = new CronTriggerImpl { CronExpressionString = task.CronExpressionString, Name = task.TaskID.ToString(), Description = task.TaskName }; _scheduler.ScheduleJob(job, trigger); if (task.Status == TaskStatus.STOP) { JobKey jk = new JobKey(task.TaskID.ToString()); _scheduler.PauseJob(jk); } else { List返回任务trigger ///list = GetNextFireTime(task.CronExpressionString, 5); foreach (var time in list) { LogHelper.WriteLog(time.ToString(CultureInfo.InvariantCulture)); } } } else { throw new Exception(task.CronExpressionString + "不是正确的Cron表达式,无法启动该任务!"); } } /// /// 初始化 远程Quartz服务器中的,各个Scheduler实例。 /// 提供给远程管理端的后台,用户获取Scheduler实例的信息。 /// public static void InitRemoteScheduler() { try { NameValueCollection properties = new NameValueCollection { ["quartz.scheduler.instanceName"] = "ExampleQuartzScheduler", ["quartz.scheduler.proxy"] = "true", ["quartz.scheduler.proxy.address"] =string.Format("{0}://{1}:{2}/QuartzScheduler", Scheme, Server, Port) }; ISchedulerFactory sf = new StdSchedulerFactory(properties); _scheduler = sf.GetScheduler(); } catch (Exception ex) { throw new Exception(ex.StackTrace); } } ////// 删除现有任务 /// /// public static void DeleteJob(string jobKey) { try { JobKey jk = new JobKey(jobKey); if (_scheduler.CheckExists(jk)) { //任务已经存在则删除 _scheduler.DeleteJob(jk); } } catch (Exception ex) { throw new Exception(ex.Message); } } ////// 暂停任务 /// /// public static void PauseJob(string jobKey) { try { JobKey jk = new JobKey(jobKey); if (_scheduler.CheckExists(jk)) { //任务已经存在则暂停任务 _scheduler.PauseJob(jk); } } catch (Exception ex) { throw new Exception(ex.Message); } } ////// 恢复运行暂停的任务 /// /// 任务key public static void ResumeJob(string jobKey) { try { JobKey jk = new JobKey(jobKey); if (_scheduler.CheckExists(jk)) { //任务已经存在则暂停任务 _scheduler.ResumeJob(jk); } } catch (Exception ex) { throw new Exception(ex.Message); } } ////// 获取类的属性、方法 /// /// 程序集 /// 类名 private static Type GetClassInfo(string assemblyName, string className) { try { assemblyName = FileHelper.GetAbsolutePath(assemblyName + ".dll"); Assembly assembly = null; if (!AssemblyDict.TryGetValue(assemblyName, out assembly)) { assembly = Assembly.LoadFrom(assemblyName); AssemblyDict[assemblyName] = assembly; } Type type = assembly.GetType(className, true, true); return type; } catch (Exception ex) { throw new Exception(ex.Message); } } ////// 停止任务调度 /// public static void StopSchedule() { try { //判断调度是否已经关闭 if (!_scheduler.IsShutdown) { //等待任务运行完成 _scheduler.Shutdown(true); } } catch (Exception ex) { throw new Exception(ex.Message); } } ////// 校验字符串是否为正确的Cron表达式 /// /// 带校验表达式 ///public static bool ValidExpression(string cronExpression) { return CronExpression.IsValidExpression(cronExpression); } /// /// 获取任务在未来周期内哪些时间会运行 /// /// Cron表达式 /// 运行次数 ///运行时间段 public static ListGetNextFireTime(string CronExpressionString, int numTimes) { if (numTimes < 0) { throw new Exception("参数numTimes值大于等于0"); } //时间表达式 ITrigger trigger = TriggerBuilder.Create().WithCronSchedule(CronExpressionString).Build(); IList dates = TriggerUtils.ComputeFireTimes(trigger as IOperableTrigger, null, numTimes); List list = new List (); foreach (DateTimeOffset dtf in dates) { list.Add(TimeZoneInfo.ConvertTimeFromUtc(dtf.DateTime, TimeZoneInfo.Local)); } return list; } public static object CurrentTaskList() { throw new NotImplementedException(); } /// /// 获取当前执行的Task 对象 /// /// ///public static TaskModel GetTaskDetail(IJobExecutionContext context) { TaskModel task = new TaskModel(); if (context != null) { task.TaskID = Guid.Parse(context.Trigger.Key.Name); task.TaskName = context.Trigger.Description; task.RecentRunTime = DateTime.Now; task.TaskParam = context.JobDetail.JobDataMap.Get("TaskParam") != null ? context.JobDetail.JobDataMap.Get("TaskParam").ToString() : ""; } return task; } }
2.设置执行中的任务:
public class TaskBll { private readonly TaskDAL _dal = new TaskDAL(); ////// 获取任务列表 /// /// /// ///public PageOf GetTaskList(int pageIndex, int pageSize) { return _dal.GetTaskList(pageIndex, pageSize); } /// /// 读取数据库中全部的任务 /// ///public List GetAllTaskList() { return _dal.GetAllTaskList(); } /// /// /// /// ///public TaskModel GetById(string taskId) { throw new NotImplementedException(); } /// /// 删除任务 /// /// ///public bool DeleteById(string taskId) { return _dal.UpdateTaskStatus(taskId, -1); } /// /// 修改任务 /// /// /// ///public bool UpdateTaskStatus(string taskId, int status) { return _dal.UpdateTaskStatus(taskId, status); } /// /// 修改任务的下次启动时间 /// /// /// ///public bool UpdateNextFireTime(string taskId, DateTime nextFireTime) { return _dal.UpdateNextFireTime(taskId, nextFireTime); } /// /// 根据任务Id 修改 上次运行时间 /// /// /// ///public bool UpdateRecentRunTime(string taskId, DateTime recentRunTime) { return _dal.UpdateRecentRunTime(taskId, recentRunTime); } /// /// 根据任务Id 获取任务 /// /// ///public TaskModel GetTaskById(string taskId) { return _dal.GetTaskById(taskId); } /// /// 添加任务 /// /// ///public bool Add(TaskModel task) { return _dal.Add(task); } /// /// 修改任务 /// /// ///public bool Edit(TaskModel task) { return _dal.Edit(task); } }
3.任务实体:
////// 任务实体 /// public class TaskModel { ////// 任务ID /// public Guid TaskID { get; set; } ////// 任务名称 /// public string TaskName { get; set; } ////// 任务执行参数 /// public string TaskParam { get; set; } ////// 运行频率设置 /// public string CronExpressionString { get; set; } ////// 任务运频率中文说明 /// public string CronRemark { get; set; } ////// 任务所在DLL对应的程序集名称 /// public string AssemblyName { get; set; } ////// 任务所在类 /// public string ClassName { get; set; } public TaskStatus Status { get; set; } ////// 任务创建时间 /// public DateTime? CreatedTime { get; set; } ////// 任务修改时间 /// public DateTime? ModifyTime { get; set; } ////// 任务最近运行时间 /// public DateTime? RecentRunTime { get; set; } ////// 任务下次运行时间 /// public DateTime? NextFireTime { get; set; } ////// 任务备注 /// public string Remark { get; set; } ////// 是否删除 /// public int IsDelete { get; set; } }
4.配置文件:
# You can configure your scheduler in eitherconfiguration section # or in quartz properties file # Configuration section has precedence quartz.scheduler.instanceName = ExampleQuartzScheduler # configure thread pool info quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz quartz.threadPool.threadCount = 10 quartz.threadPool.threadPriority = Normal # job initialization plugin handles our xml reading, without it defaults are used # quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz # quartz.plugin.xml.fileNames = ~/quartz_jobs.xml # export this server to remoting context quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz quartz.scheduler.exporter.port = 555 quartz.scheduler.exporter.bindName = QuartzScheduler quartz.scheduler.exporter.channelType = tcp quartz.scheduler.exporter.channelName = httpQuartz
四.总结:
在项目中比较多的使用到定时任务的功能,今天的介绍的组件可以很好的完成一些定时任务的要求。这篇文章主要是作为引子,简单的介绍了组件的背景和组件的使用方式,如果项目中需要使用,可以进行更加深入的了解。
关于如何使用DotNet任务调度组件Quartz.NET就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流