千家信息网

如何使用DotNet任务调度组件Quartz.NET

发表于:2024-11-22 作者:千家信息网编辑
千家信息网最后更新 2024年11月22日,这篇文章给大家介绍如何使用DotNet任务调度组件Quartz.NET,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。很多的软件项目中都会使用到定时任务、定时轮询数据库同步,定时邮
千家信息网最后更新 2024年11月22日如何使用DotNet任务调度组件Quartz.NET

这篇文章给大家介绍如何使用DotNet任务调度组件Quartz.NET,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

很多的软件项目中都会使用到定时任务、定时轮询数据库同步,定时邮件通知等功能。.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实例。

  ///     /// 返回此工厂生成的调度程序的句柄。    ///     ///     ///如果方法之一没有先前调用,然后是默认(no-arg)方法将被这个方法调用。    ///     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;    }
public interface ISchedulerFactory  {    ///     /// Returns handles to all known Schedulers (made by any SchedulerFactory    /// within this app domain.).    ///     ICollection AllSchedulers { 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 Dictionary AssemblyDict = 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);      }    }    ///     /// 启用任务    /// 任务信息    /// 是否删除原有任务    /// 返回任务trigger    ///     public static void ScheduleJob(TaskModel task, bool isDeleteOldTask = false)    {      if (isDeleteOldTask)      {        //先删除现有已存在任务        DeleteJob(task.TaskID.ToString());      }      //验证是否正确的Cron表达式      if (Valid_Expression(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 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 Valid_Expression(string cronExpression)    {      return CronExpression.IsValid_Expression(cronExpression);    }    ///     /// 获取任务在未来周期内哪些时间会运行    ///     /// Cron表达式    /// 运行次数    /// 运行时间段    public static List GetNextFireTime(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 either  configuration section# or in quartz properties file# Configuration section has precedencequartz.scheduler.instanceName = ExampleQuartzScheduler# configure thread pool infoquartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartzquartz.threadPool.threadCount = 10quartz.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 contextquartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartzquartz.scheduler.exporter.port = 555quartz.scheduler.exporter.bindName = QuartzSchedulerquartz.scheduler.exporter.channelType = tcpquartz.scheduler.exporter.channelName = httpQuartz

四.总结:

在项目中比较多的使用到定时任务的功能,今天的介绍的组件可以很好的完成一些定时任务的要求。这篇文章主要是作为引子,简单的介绍了组件的背景和组件的使用方式,如果项目中需要使用,可以进行更加深入的了解。

关于如何使用DotNet任务调度组件Quartz.NET就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

0