最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

详解免费开源的DotNet任务调度组件Quartz.NET(.NET组件介绍之五)

来源:动视网 责编:小采 时间:2020-11-27 22:36:08
文档

详解免费开源的DotNet任务调度组件Quartz.NET(.NET组件介绍之五)

详解免费开源的DotNet任务调度组件Quartz.NET(.NET组件介绍之五):很多的软件项目中都会使用到定时任务、定时轮询数据库同步,定时邮件通知等功能。.NET Framework具有内置定时器功能,通过System.Timers.Timer类。在使用Timer类需要面对的问题:计时器没有持久化机制;计时器具有不灵活的计划(仅能设置开始时间和重复间
推荐度:
导读详解免费开源的DotNet任务调度组件Quartz.NET(.NET组件介绍之五):很多的软件项目中都会使用到定时任务、定时轮询数据库同步,定时邮件通知等功能。.NET Framework具有内置定时器功能,通过System.Timers.Timer类。在使用Timer类需要面对的问题:计时器没有持久化机制;计时器具有不灵活的计划(仅能设置开始时间和重复间


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

 /// <summary>
 /// 返回此工厂生成的调度程序的句柄。
 /// </summary>
 /// <remarks>
 ///如果<see cref =“Initialize()”/>方法之一没有先前调用,然后是默认(no-arg)<see cref =“Initialize()”/>方法将被这个方法调用。
 /// </remarks>
 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
 {
 /// <summary>
 /// Returns handles to all known Schedulers (made by any SchedulerFactory
 /// within this app domain.).
 /// </summary>
 ICollection<IScheduler> AllSchedulers { get; }

 /// <summary>
 /// Returns a client-usable handle to a <see cref="IScheduler" />.
 /// </summary>
 IScheduler GetScheduler();

 /// <summary>
 /// Returns a handle to the Scheduler with the given name, if it exists.
 /// </summary>
 IScheduler GetScheduler(string schedName);
 }

2.JobDetailImpl:传递给定作业实例的详细信息属性。

/// <summary>
 /// 获取或设置与<see cref =“IJob”/>相关联的<see cref =“JobDataMap”/>。
 /// </summary>
 public virtual JobDataMap JobDataMap
 {
 get
 {
 if (jobDataMap == null)
 {
 jobDataMap = new JobDataMap();
 }
 return jobDataMap;
 }

 set { jobDataMap = value; }
 }

3.JobKey:键由名称和组组成,名称必须是唯一的,在组内。 如果只指定一个组,则默认组将使用名称。

 [Serializable]
 public sealed class JobKey : Key<JobKey>
 {
 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():

/// <summary> 
 /// 使用初始化<see cref =“ISchedulerFactory”/>
 ///给定键值集合对象的内容。
 /// </summary>
 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<string> supportedKeys = new List<string>();
 List<FieldInfo> fields = new List<FieldInfo>(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.任务处理帮助类:

 /// <summary>
 /// 任务处理帮助类
 /// </summary>
 public class QuartzHelper
 {
 public QuartzHelper() { }

 public QuartzHelper(string quartzServer, string quartzPort)
 {
 Server = quartzServer;
 Port = quartzPort;
 }

 /// <summary>
 /// 锁对象
 /// </summary>
 private static readonly object Obj = new object();

 /// <summary>
 /// 方案
 /// </summary>
 private const string Scheme = "tcp";

 /// <summary>
 /// 服务器的地址
 /// </summary>
 public static string Server { get; set; }

 /// <summary>
 /// 服务器的端口
 /// </summary>
 public static string Port { get; set; }

 /// <summary>
 /// 缓存任务所在程序集信息
 /// </summary>
 private static readonly Dictionary<string, Assembly> AssemblyDict = new Dictionary<string, Assembly>();

 /// <summary>
 /// 程序调度
 /// </summary>
 private static IScheduler _scheduler;

 /// <summary>
 /// 初始化任务调度对象
 /// </summary>
 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);
 }
 }

 /// <summary>
 /// 启用任务调度
 /// 启动调度时会把任务表中状态为“执行中”的任务加入到任务调度队列中
 /// </summary>
 public static void StartScheduler()
 {
 try
 {
 if (_scheduler.IsStarted) return;
 //添加全局监听
 _scheduler.ListenerManager.AddTriggerListener(new CustomTriggerListener(), GroupMatcher<TriggerKey>.AnyGroup());
 _scheduler.Start();

 //获取所有执行中的任务
 List<TaskModel> 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);
 }
 }

 /// <summary>
 /// 启用任务
 /// <param name="task">任务信息</param>
 /// <param name="isDeleteOldTask">是否删除原有任务</param>
 /// <returns>返回任务trigger</returns>
 /// </summary>
 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<DateTime> list = GetNextFireTime(task.CronExpressionString, 5);
 foreach (var time in list)
 {
 LogHelper.WriteLog(time.ToString(CultureInfo.InvariantCulture));
 }
 }
 }
 else
 {
 throw new Exception(task.CronExpressionString + "不是正确的Cron表达式,无法启动该任务!");
 }
 }


 /// <summary>
 /// 初始化 远程Quartz服务器中的,各个Scheduler实例。
 /// 提供给远程管理端的后台,用户获取Scheduler实例的信息。
 /// </summary>
 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);
 }
 }

 /// <summary>
 /// 删除现有任务
 /// </summary>
 /// <param name="jobKey"></param>
 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);
 }
 }

 

 /// <summary>
 /// 暂停任务
 /// </summary>
 /// <param name="jobKey"></param>
 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);
 }
 }

 /// <summary>
 /// 恢复运行暂停的任务
 /// </summary>
 /// <param name="jobKey">任务key</param>
 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);
 }
 }

 /// <summary> 
 /// 获取类的属性、方法 
 /// </summary> 
 /// <param name="assemblyName">程序集</param> 
 /// <param name="className">类名</param> 
 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);
 }
 }

 /// <summary>
 /// 停止任务调度
 /// </summary>
 public static void StopSchedule()
 {
 try
 {
 //判断调度是否已经关闭
 if (!_scheduler.IsShutdown)
 {
 //等待任务运行完成
 _scheduler.Shutdown(true);
 }
 }
 catch (Exception ex)
 {
 throw new Exception(ex.Message);
 }
 }

 /// <summary>
 /// 校验字符串是否为正确的Cron表达式
 /// </summary>
 /// <param name="cronExpression">带校验表达式</param>
 /// <returns></returns>
 public static bool ValidExpression(string cronExpression)
 {
 return CronExpression.IsValidExpression(cronExpression);
 }

 /// <summary>
 /// 获取任务在未来周期内哪些时间会运行
 /// </summary>
 /// <param name="CronExpressionString">Cron表达式</param>
 /// <param name="numTimes">运行次数</param>
 /// <returns>运行时间段</returns>
 public static List<DateTime> GetNextFireTime(string CronExpressionString, int numTimes)
 {
 if (numTimes < 0)
 {
 throw new Exception("参数numTimes值大于等于0");
 }
 //时间表达式
 ITrigger trigger = TriggerBuilder.Create().WithCronSchedule(CronExpressionString).Build();
 IList<DateTimeOffset> dates = TriggerUtils.ComputeFireTimes(trigger as IOperableTrigger, null, numTimes);
 List<DateTime> list = new List<DateTime>();
 foreach (DateTimeOffset dtf in dates)
 {
 list.Add(TimeZoneInfo.ConvertTimeFromUtc(dtf.DateTime, TimeZoneInfo.Local));
 }
 return list;
 }


 public static object CurrentTaskList()
 {
 throw new NotImplementedException();
 }

 /// <summary>
 /// 获取当前执行的Task 对象
 /// </summary>
 /// <param name="context"></param>
 /// <returns></returns>
 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();

 /// <summary>
 /// 获取任务列表
 /// </summary>
 /// <param name="pageIndex"></param>
 /// <param name="pageSize"></param>
 /// <returns></returns>
 public PageOf<TaskModel> GetTaskList(int pageIndex, int pageSize)
 {
 return _dal.GetTaskList(pageIndex, pageSize);
 }

 /// <summary>
 /// 读取数据库中全部的任务
 /// </summary>
 /// <returns></returns>
 public List<TaskModel> GetAllTaskList()
 {
 return _dal.GetAllTaskList();
 }

 /// <summary>
 /// 
 /// </summary>
 /// <param name="taskId"></param>
 /// <returns></returns>
 public TaskModel GetById(string taskId)
 {
 throw new NotImplementedException();
 }

 /// <summary>
 /// 删除任务
 /// </summary>
 /// <param name="taskId"></param>
 /// <returns></returns>
 public bool DeleteById(string taskId)
 {
 return _dal.UpdateTaskStatus(taskId, -1);
 }

 /// <summary>
 /// 修改任务
 /// </summary>
 /// <param name="taskId"></param>
 /// <param name="status"></param>
 /// <returns></returns>
 public bool UpdateTaskStatus(string taskId, int status)
 {
 return _dal.UpdateTaskStatus(taskId, status);
 }

 /// <summary>
 /// 修改任务的下次启动时间
 /// </summary>
 /// <param name="taskId"></param>
 /// <param name="nextFireTime"></param>
 /// <returns></returns>
 public bool UpdateNextFireTime(string taskId, DateTime nextFireTime)
 {
 return _dal.UpdateNextFireTime(taskId, nextFireTime);
 }

 /// <summary>
 /// 根据任务Id 修改 上次运行时间
 /// </summary>
 /// <param name="taskId"></param>
 /// <param name="recentRunTime"></param>
 /// <returns></returns>
 public bool UpdateRecentRunTime(string taskId, DateTime recentRunTime)
 {
 return _dal.UpdateRecentRunTime(taskId, recentRunTime);
 }

 /// <summary>
 /// 根据任务Id 获取任务
 /// </summary>
 /// <param name="taskId"></param>
 /// <returns></returns>
 public TaskModel GetTaskById(string taskId)
 {
 return _dal.GetTaskById(taskId);
 }

 /// <summary>
 /// 添加任务
 /// </summary>
 /// <param name="task"></param>
 /// <returns></returns>
 public bool Add(TaskModel task)
 {
 return _dal.Add(task);
 }

 /// <summary>
 /// 修改任务
 /// </summary>
 /// <param name="task"></param>
 /// <returns></returns>
 public bool Edit(TaskModel task)
 {
 return _dal.Edit(task);
 }
 }

3.任务实体:

/// <summary>
 /// 任务实体
 /// </summary>
 public class TaskModel
 {
 /// <summary>
 /// 任务ID
 /// </summary>
 public Guid TaskID { get; set; }

 /// <summary>
 /// 任务名称
 /// </summary>
 public string TaskName { get; set; }

 /// <summary>
 /// 任务执行参数
 /// </summary>
 public string TaskParam { get; set; }

 /// <summary>
 /// 运行频率设置
 /// </summary>
 public string CronExpressionString { get; set; }

 /// <summary>
 /// 任务运频率中文说明
 /// </summary>
 public string CronRemark { get; set; }

 /// <summary>
 /// 任务所在DLL对应的程序集名称
 /// </summary>
 public string AssemblyName { get; set; }

 /// <summary>
 /// 任务所在类
 /// </summary>
 public string ClassName { get; set; }

 public TaskStatus Status { get; set; }

 /// <summary>
 /// 任务创建时间
 /// </summary>
 public DateTime? CreatedTime { get; set; }

 /// <summary>
 /// 任务修改时间
 /// </summary>
 public DateTime? ModifyTime { get; set; }

 /// <summary>
 /// 任务最近运行时间
 /// </summary>
 public DateTime? RecentRunTime { get; set; }

 /// <summary>
 /// 任务下次运行时间
 /// </summary>
 public DateTime? NextFireTime { get; set; }

 /// <summary>
 /// 任务备注
 /// </summary>
 public string Remark { get; set; }

 /// <summary>
 /// 是否删除
 /// </summary>
 public int IsDelete { get; set; }
 }

 4.配置文件:

 # You can configure your scheduler in either <quartz> configuration 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(.NET组件介绍之五)

详解免费开源的DotNet任务调度组件Quartz.NET(.NET组件介绍之五):很多的软件项目中都会使用到定时任务、定时轮询数据库同步,定时邮件通知等功能。.NET Framework具有内置定时器功能,通过System.Timers.Timer类。在使用Timer类需要面对的问题:计时器没有持久化机制;计时器具有不灵活的计划(仅能设置开始时间和重复间
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top