0.Quartz.NET 3.x Tutorial
Quartz.NET - Quartz.NET 3.x Tutorial
1.简单的Quartz例子调用
Quartz.NET实现作业调度
2.Quartz的调度框架,包含可视化的界面的简单使用
Quartz.net 开源job调度框架(一)
3.CronTrigger Tutorial
Quartz.NET - CronTrigger Tutorial
4.CrystalQuartz
Net作业调度(二) -CrystalQuartz远程管理
5.Quartz Instance
5.1 Clinet
5.1.1 使用到的相关实体定义
[Description("触发器基础信息类")] public class TriggerModel { #region CommonInfo /// <summary> /// 触发器名称 /// </summary> public string name { get; set; } /// <summary> /// 触发器所在组 /// </summary> public string group { get; set; } /// <summary> /// 触发器描述 /// </summary> public string description { get; set; } /// <summary> /// 触发器开始执行时间 /// </summary> public string startTime { get; set; } /// <summary> /// 触发器结束执行时间 /// </summary> public string endTime { get; set; } /// <summary> /// 触发器所使用的日历过滤器 /// </summary> public string calendarName { get; set; } /// <summary> /// 触发器下次执行时间 /// </summary> public string nextFireTime { get; set; } /// <summary> /// 触发器上次执行时间 /// </summary> public string previousFireTime { get; set; } /// <summary> /// 触发器的触发规则 /// </summary> public string rule { get; set; } /// <summary> /// 触发器触发次数 /// </summary> public int? timesTriggered { get; set; } /// <summary> /// 触发器类型 /// </summary> public string triggerType { get; set; } /// <summary> /// 触发器状态(Normal = 0,Paused = 1,Complete = 2,Error = 3,Blocked = 4,None = 5,) /// </summary> public int triggerState { get; set; } /// <summary> /// 关联Job组名 /// </summary> public string jobDetailGroup { get; set; } /// <summary> /// 关联Job组名 /// </summary> public string jobDetailName { get; set; } #endregion #region SimpleTrigger /// <summary> /// 触发器重复执行次数 /// </summary> public int repeatCount { get; set; } /// <summary> /// 触发器执行周期 /// </summary> public string repeatInterval { get; set; } #endregion #region DailyTimeTrigger /// <summary> /// 日常触发器每天开始触发时间 /// </summary> public string startTimeOfDay { get; set; } /// <summary> /// 日常触发器每天结束触发时间 /// </summary> public string endTimeOfDay { get; set; } /// <summary> /// 周中天 /// </summary> public string daysOfWeek { get; set; } #endregion #region CronTrigger /// <summary> /// Cron表达式 /// </summary> public string cronExpression { get; set; } #endregion } /// <summary> /// Trigger操作类 /// </summary> public class TriggerOperateModel { /// <summary> /// 名称 /// </summary> public string name { get; set; } /// <summary> /// 组 /// </summary> public string group { get; set; } /// <summary> /// 操作类型 /// </summary> public int operateType { get; set; } }
public class JobMapModel { public string key { get; set; } public string value { get; set; } }
[Description("作业信息类")] public class JobModel { /// <summary> /// 作业所在组 /// </summary> public string group { get; set; } /// <summary> /// 作业名称 /// </summary> public string name { get; set; } /// <summary> /// 作业描述 /// </summary> public string description { get; set; } /// <summary> /// 作业类型 /// </summary> public string type { get; set; } /// <summary> /// 作业是否允许孤立存在 /// </summary> public bool durable { get; set; } /// <summary> /// 工作数据字段 /// </summary> public List<JobMapModel> jobDataMap { get; set; } /// <summary> /// /// </summary> public bool requestsRecovery { get; set; } /// <summary> /// 并发控制 /// </summary> public bool concurrentExecutionDisallowed { get; set; } /// <summary> /// 执行后JobData数据是否能持久 /// </summary> public bool persistJobDataAfterExecution { get; set; } /// <summary> /// 与JobInfo关联的所有TriggerInfos /// </summary> public List<TriggerModel> triggerInfos { get; set; } }
public class JobTriggerModel { /// <summary> /// 作业名称 /// </summary> public string jobName { get; set; } /// <summary> /// 触发器名称 /// </summary> public string triggerName { get; set; } /// <summary> /// 触发器所在组 /// </summary> public string triggerGroup { get; set; } /// <summary> /// 触发器开始执行时间 /// </summary> public string startTime { get; set; } /// <summary> /// 触发器结束执行时间 /// </summary> //public string endTime { get; set; } /// <summary> /// 触发器下次执行时间 /// </summary> public string nextFireTime { get; set; } /// <summary> /// 触发器上次执行时间 /// </summary> public string previousFireTime { get; set; } }
5.1.2 Quartz帮助类
using Quartz; using Quartz.Impl; using Quartz.Impl.Matchers; using Quartz.Impl.Triggers; using Models.QuartzTimedTask; using System; using System.Collections.Generic; using System.Linq; using System.Web; public class QuartzHelper { #region SchedulerOperate /// <summary> /// 创建Scheduler实例 /// </summary> /// <returns></returns> public IScheduler CreateScheduler() { ISchedulerFactory schedulerFactory = new StdSchedulerFactory(); return schedulerFactory.GetScheduler(); } #endregion #region private private bool AddOrEditJobDetail(JobModel jobInfo, bool isReplace) { var result = false; var scheduler = CreateScheduler(); //job var jobDetail = QuartzConvert.JobInfoToJobDetailImpl(jobInfo); var isCheckExistsOk = scheduler.CheckExists(jobDetail.Key); isCheckExistsOk = isReplace ? !isCheckExistsOk : isCheckExistsOk; if (jobDetail == null || isCheckExistsOk) return false; //add job scheduler.AddJob(jobDetail, isReplace, !jobDetail.Durable); result = scheduler.CheckExists(jobDetail.Key); return result; } #endregion /// <summary> /// 添加新的JobDetail /// </summary> /// <param name="jonInfo"></param> /// <returns></returns> public bool AddNewJobDetail(JobModel jobInfo) { return AddOrEditJobDetail(jobInfo, false); } /// <summary> /// 编辑JobDetail /// </summary> /// <param name="jonInfo"></param> /// <returns></returns> public bool EditJobDetail(JobModel jobInfo) { return AddOrEditJobDetail(jobInfo, true); } /// <summary> /// 删除JobDetail /// </summary> /// <param name="name"></param> /// <param name="group"></param> /// <returns></returns> public bool DeleteJobDetail(string name, string group) { var scheduler = CreateScheduler(); var ifExecute = scheduler.GetCurrentlyExecutingJobs() .Select(z => z.JobDetail) .Where(z => z.Key.Name == name && z.Key.Group == group) .ToList() .Count > 0; if (ifExecute) return false; var result = scheduler.DeleteJob(new JobKey(name, group)); return result; } #region trigger /// <summary> /// 添加Trigger /// </summary> /// <param name="triggerInfo"></param> /// <returns></returns> public bool AddNewTriggerWithJobDetail(TriggerModel triggerInfo) { var scheduler = CreateScheduler(); var trigger = scheduler.GetTrigger(new TriggerKey(triggerInfo.name, triggerInfo.group)); if (trigger != null) return false; var job = scheduler.GetJobDetail(new JobKey(triggerInfo.jobDetailName, triggerInfo.jobDetailGroup)); if (job == null) return false; trigger = QuartzConvert.TriggerInfoToCronTrigger(triggerInfo); if (trigger == null) return false; var abstractTrigger = trigger as AbstractTrigger; if (abstractTrigger == null) return false; abstractTrigger.JobKey = job.Key; scheduler.ScheduleJob(abstractTrigger); abstractTrigger.StartTimeUtc = abstractTrigger.StartTimeUtc.AddSeconds(1); scheduler.PauseTrigger(abstractTrigger.Key); return true; } public bool DeleteTrigger(string name, string group) { var scheduler = CreateScheduler(); var ifExecute = scheduler.GetCurrentlyExecutingJobs() .Select(z => z.Trigger) .Where(z => z.Key.Name == name && z.Key.Group == group) .ToList() .Count > 0; if (ifExecute) return false; var result = scheduler.UnscheduleJob(new TriggerKey(name, group)); return result; } public void PauseTrigger(TriggerOperateModel operate) { var scheduler = CreateScheduler(); if (scheduler == null) scheduler = CreateScheduler(); if (operate.operateType != 0 && operate.operateType != 1) return; scheduler.PauseTrigger(new TriggerKey(operate.name, operate.group)); } public void ResumeTrigger(TriggerOperateModel operate) { var scheduler = CreateScheduler(); if (scheduler == null) scheduler = CreateScheduler(); if (operate.operateType != 0 && operate.operateType != 1) return; scheduler.ResumeTrigger(new TriggerKey(operate.name, operate.group)); } //<summary> //根据JobDetail获取 //</summary> //<param name="jobKey"></param> //<returns></returns> public List<TriggerModel> GetTriggerInfosByJobDetail(JobKey jobKey) { var scheduler = CreateScheduler(); var triggers = scheduler.GetTriggersOfJob(jobKey).ToList(); var triggerInfos = new List<TriggerModel>(); triggers.ForEach(z => { var trigger = QuartzConvert.CronTriggerImplToTriggerInfo((CronTriggerImpl)z); if (trigger == null) return; trigger.triggerState = (int)scheduler.GetTriggerState(z.Key); triggerInfos.Add(trigger); }); return triggerInfos; } #endregion /// <summary> /// 获取所有任务 /// </summary> /// <returns></returns> public List<JobModel> GetAllJobDetails() { var result = new List<JobModel>(); var scheduler = CreateScheduler(); var groupNameList = scheduler.GetJobGroupNames(); foreach (var groupName in groupNameList) { var jobKeyList = scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)).ToList(); foreach (var jobKey in jobKeyList) { var jobDetail = scheduler.GetJobDetail(jobKey) as JobDetailImpl; result.Add(QuartzConvert.JobDetailImplToJobInfo(jobDetail, this)); } } return result; } public List<JobTriggerModel> GetJobTriggerList() { var result = new List<JobTriggerModel>(); var scheduler = CreateScheduler(); var groupNameList = scheduler.GetTriggerGroupNames(); foreach (var groupName in groupNameList) { var triggerKeyList = scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupEquals(groupName)); foreach (var triggerKey in triggerKeyList) { var triggerDetail = scheduler.GetTrigger(triggerKey) as CronTriggerImpl; var jobTrigger = QuartzConvert.CronTriggerImplToJobTriggerModel(triggerDetail); result.Add(jobTrigger); } } return result; } /// <summary> /// 立即执行一次 /// </summary> /// <param name="jobKey"></param> public void ExcuteJobNow(string name, string group) { var scheduler = CreateScheduler(); var jobKey = new JobKey(name, group); var jobDetail = scheduler.GetJobDetail(jobKey); var excuteNowTrigger = TriggerBuilder.Create().WithIdentity("ExcuteNowTrigger", "ExcuteNowGroup").ForJob(jobDetail).StartNow().Build(); scheduler.ScheduleJob(excuteNowTrigger); } }
using Quartz; using Quartz.Impl; using Quartz.Impl.Triggers; using QuartzTimedTaskJobs; using QuartzTimedTask.Models; using System; using System.Collections.Generic; using System.Linq; using System.Web; public class QuartzConvert { private static Type JobType = typeof(FireTimingTaskJob); /// <summary> /// JobInfo转JobDetail /// </summary> /// <param name="jobInfo"></param> /// <returns></returns> public static JobDetailImpl JobInfoToJobDetailImpl(JobModel jobInfo) { var jobDetail = new JobDetailImpl { Name = jobInfo.name, Group = jobInfo.group, Key = new JobKey(jobInfo.name, jobInfo.group), Description = jobInfo.description, JobType = JobType, RequestsRecovery = jobInfo.requestsRecovery, Durable = jobInfo.durable }; if (jobInfo.jobDataMap != null && jobInfo.jobDataMap.Count > 0) { jobInfo.jobDataMap.ForEach(z => { jobDetail.JobDataMap.Add(z.key, z.value); }); } return jobDetail; } /// <summary> /// TriggerModel转CronTriggerImpl(只支持CronTriggerImpl) /// </summary> /// <param name="triggerInfo"></param> /// <returns></returns> public static CronTriggerImpl TriggerInfoToCronTrigger(TriggerModel triggerInfo) { var cronTrigger = new CronTriggerImpl { Name = triggerInfo.name, Group = triggerInfo.group, CalendarName = triggerInfo.calendarName, Description = triggerInfo.description, CronExpressionString = CronExpression.IsValidExpression(triggerInfo.cronExpression) ? triggerInfo.cronExpression : "", }; #region [StartTime\EndTime]-Operate DateTime startTime, endTime; var ifsuccess = DateTime.TryParse(triggerInfo.startTime, out startTime); if (!ifsuccess) startTime = DateTime.Now; cronTrigger.StartTimeUtc = startTime; ifsuccess = DateTime.TryParse(triggerInfo.endTime, out endTime); if (ifsuccess) cronTrigger.EndTimeUtc = endTime; #endregion return cronTrigger; } public static JobModel JobDetailImplToJobInfo(JobDetailImpl jobDetailImpl, QuartzHelper qh) { var jobInfo = new JobModel { name = jobDetailImpl.Name, group = jobDetailImpl.Group, description = jobDetailImpl.Description, type = jobDetailImpl.JobType.Name, requestsRecovery = jobDetailImpl.RequestsRecovery, durable = jobDetailImpl.Durable, concurrentExecutionDisallowed = jobDetailImpl.ConcurrentExecutionDisallowed, persistJobDataAfterExecution = jobDetailImpl.PersistJobDataAfterExecution, jobDataMap = new List<JobMapModel>(), }; //if (jobDetailImpl.JobDataMap != null && jobDetailImpl.JobDataMap.Count > 0) //{ // jobDetailImpl.JobDataMap.ForEach(z => // { // jobInfo.jobDataMap.Add(new JobMapModel { key = z.Key, value = z.Value.ToString() }); // }); //} jobInfo.triggerInfos = qh.GetTriggerInfosByJobDetail(jobDetailImpl.Key); return jobInfo; } /// <summary> /// CronTriggerImpl转TriggerInfo /// </summary> /// <param name="cronTriggerImpl"></param> /// <returns></returns> public static TriggerModel CronTriggerImplToTriggerInfo(CronTriggerImpl cronTriggerImpl) { var triggerInfo = new TriggerModel { name = cronTriggerImpl.Name, group = cronTriggerImpl.Group, calendarName = cronTriggerImpl.CalendarName, description = cronTriggerImpl.Description, startTime = cronTriggerImpl.StartTimeUtc.ToString(), endTime = cronTriggerImpl.EndTimeUtc != null ? ((DateTimeOffset)cronTriggerImpl.EndTimeUtc).ToString() : "", cronExpression = cronTriggerImpl.CronExpressionString, rule = cronTriggerImpl.CronExpressionString, triggerType = "CronTriggerImpl", previousFireTime = cronTriggerImpl.GetPreviousFireTimeUtc() != null ? cronTriggerImpl.GetPreviousFireTimeUtc().ToString() : "", nextFireTime = cronTriggerImpl.GetNextFireTimeUtc() != null ? cronTriggerImpl.GetNextFireTimeUtc().ToString() : "" }; return triggerInfo; } private static string FormatDTO(DateTimeOffset? dto) { return dto.HasValue ? dto.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss") : string.Empty; } public static JobTriggerModel CronTriggerImplToJobTriggerModel(CronTriggerImpl cronTriggerImpl) { var jobTriggerInfo = new JobTriggerModel { jobName = cronTriggerImpl.JobName, triggerGroup = cronTriggerImpl.Group, triggerName = cronTriggerImpl.Name, startTime = FormatDTO(cronTriggerImpl.StartTimeUtc), previousFireTime = FormatDTO(cronTriggerImpl.GetPreviousFireTimeUtc()), nextFireTime = FormatDTO(cronTriggerImpl.GetNextFireTimeUtc()) }; return jobTriggerInfo; } }
5.1.3 config文件
<configuration> <configSections> <section name="quartz" type="System.Configuration.NameValueSectionHandler" /> </configSections> <quartz> <add key="quartz.scheduler.instanceName" value="QuartzTimedTaskScheduler" /> <add key="quartz.scheduler.proxy" value="true" /> <add key="quartz.threadPool.threadCount" value="5" /> <add key="quartz.scheduler.proxy.address" value="tcp://127.0.0.1:556/QuartzTimedTaskScheduler" /> </quartz>
5.2 WindowsServices 服务
using Quartz; public class FireTimingTaskJob : IJob { public string TaskName { get; set; } #region IJob Members public void Execute(IJobExecutionContext context) { try { var content = string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} Task:{1},Run Trigger:{2}", DateTime.Now, TaskName, context.Trigger.SerializeObject()); Console.WriteLine(content); } catch { try { System.Threading.Thread.Sleep(1000); } catch (Exception ex) { //log.ErrorFormat("Fire task-{0} by trigger-{1} failed. Exception:{2}", TaskName, context.Trigger.Key, ex); var content = string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} Task:{1},Run Exception:{2}", DateTime.Now, TaskName, ex); Console.WriteLine(content); } } } #endregion }
using Quartz; using Quartz.Impl; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; //using QuartzTimedTaskJobs;必须要引用项目文件 namespace QuartzTimedTaskService { public class QuartzHost { IScheduler sched; public void Start() { try { ISchedulerFactory sf = new StdSchedulerFactory(); sched = sf.GetScheduler(); sched.Start(); } catch (Exception ex) { if (ex.InnerException != null) { Console.WriteLine(ex.Message); if (ex.InnerException.InnerException != null) { if (ex.InnerException.InnerException.InnerException != null) { Console.WriteLine(ex.InnerException.InnerException.InnerException.Message); } Console.WriteLine(ex.InnerException.InnerException.Message); } Console.WriteLine(ex.InnerException.Message); } Console.WriteLine(ex.Message); } } public void Stop() { if (sched != null) { sched.Shutdown(true); sched = null; } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Topshelf; using Topshelf.Extension; namespace QuartzTimedTaskService { class Program { static void Main(string[] args) { HostFactory.Run(x => { x.Service<QuartzHost>(s => { s.ConstructUsing(name => new QuartzHost()); s.WhenStarted(h => h.Start()); s.WhenStopped(h => h.Stop()); }); x.SetDescription("Smt Quartz Timed Task Service."); x.SetDisplayName("Smt Quartz Timed Task Service"); x.SetServiceName("SmtQuartzTimedTaskService"); x.SetStartTimeout(TimeSpan.FromMinutes(2)); x.SetStopTimeout(TimeSpan.FromMinutes(2)); x.RunAsLocalSystem() .StartAutomatically() .EnableServiceRecovery(r => { r.RestartService(1) .RestartService(1) .RestartService(1) .SetResetPeriod(1); }) .UseHostBuilder((environment, setting) => new CustomConsoleHostBuilder(environment, setting)); ; }); } } }
<quartz> <add key="quartz.scheduler.instanceName" value="QuartzTimedTaskScheduler" /> <add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz" /> <add key="quartz.threadPool.threadCount" value="10" /> <add key="quartz.threadPool.threadPriority" value="Normal" /> <add key="quartz.jobStore.misfireThreshold" value="60000" /> <add key="quartz.jobStore.tablePrefix" value="QRTZ_" /> <add key="quartz.jobStore.type" value="Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" /> <add key="quartz.jobStore.driverDelegateType" value="Quartz.Impl.AdoJobStore.MySQLDelegate, Quartz" /> <add key="quartz.jobStore.dataSource" value="QuartzTimedTask" /> <add key="quartz.dataSource.QuartzTimedTask.connectionString" value="Server=192.168.1.100;Database=my_QuartzTimedTask;User=sa;Password=123456;Charset=utf8;Pooling=false;" /> <add key="quartz.dataSource.QuartzTimedTask.provider" value="MySql-695" /> <!--远程输出配置--> <add key="quartz.scheduler.exporter.type" value="Quartz.Simpl.RemotingSchedulerExporter, Quartz" /> <add key="quartz.scheduler.exporter.port" value="556" /> <add key="quartz.scheduler.exporter.bindName" value="QuartzTimedTaskScheduler" /> <add key="quartz.scheduler.exporter.channelType" value="tcp" /> <add key="quartz.scheduler.exporter.channelName" value="tcpQuartz" /> <add key="quartz.scheduler.exporter.rejectRemoteRequests" value="false" /> </quartz>