Quartz.NET官網地址:https://www.quartz-scheduler.net/html
Quartz.NET文檔地址:https://www.quartz-scheduler.net/documentation/index.htmlgit
Quartz.NET
是一個開源的做業調度框架,是OpenSymphony
的 Quartz API
的.NET移植,它用C#寫成,可用於winform
和asp.net
應用中。它提供了巨大的靈活性而不犧牲簡單性。你可以用它來爲執行一個做業而建立簡單的或複雜的調度。它有不少特徵,如:數據庫支持,集羣,插件,支持cron-like
表達式等等。github
如今Quartz.NET3.0
已支持Asp.Net Core
,3.0新功能以下:sql
新功能數據庫
SQLite-Microsoft
支持Microsoft.Data.Sqlite
,舊的提供程序SQLite
也仍然有效SQL Server
內存優化表和Quartz.Impl.AdoJobStore.UpdateLockRowSemaphoreMOT
的初步支持Common.Logging
從相關性中刪除ILMerge
進程中刪除的C5集合再也不須要TimeZoneUtil
中添加對額外的自定義時區解析器功能的支持變化後端
NuGet
包Quartz.Jobs
和Quartz.Plugins
中SqlServer-20 => SqlServer
IReadOnlyCollection
,這隱藏了兩個HashSet
s和List小號LibLog
一直隱藏於內部(ILog等),就像它本來打算的那樣SimpleThreadPool
消失了,舊的擁有的線程消失了IJob
接口如今返回一個任務IList
屬性已更改成IReadOnlyList
以正確反映意圖SQL Server CE
支持已被刪除DailyCalendar
如今將日期時間用於排除的日期,並具備ISet
接口來訪問它們IObjectSerializer
有新的方法,void Initialize()
,必須實現IInterruptableJob
取消了上下文的CancellationToken
Quartz API的關鍵接口和類是:api
IScheduler
- 與調度程序交互的主要API。IJob
- 您但願由調度程序執行的組件實現的接口。IJobDetail
- 用於定義做業的實例。ITrigger
- 定義執行給定Job的時間表的組件。JobBuilder
- 用於定義/構建定義做業實例的JobDetail
實例。TriggerBuilder
- 用於定義/構建觸發器實例Install-Package Quartz
若是你想添加JSON序列化,只須要以一樣的方式添加Quartz.Serialization.Json
包。框架
using Five.QuartzNetJob.ExecuteJobTask.Service; using Quartz; using Quartz.Impl; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Text; using System.Threading.Tasks; namespace Five.QuartzNetJob.Web.Controllers { public class TestTask { public async Task StartTestAsync() { try { // 從工廠中獲取調度程序實例 NameValueCollection props = new NameValueCollection { { "quartz.serializer.type", "binary" } }; StdSchedulerFactory factory = new StdSchedulerFactory(props); IScheduler scheduler = await factory.GetScheduler(); // 開啓調度器 await scheduler.Start(); // 定義這個工做,並將其綁定到咱們的IJob實現類 IJobDetail job = JobBuilder.Create<HelloJob>() .WithIdentity("job1", "group1") .Build(); // 觸發做業當即運行,而後每10秒重複一次,無限循環 ITrigger trigger = TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartNow() .WithSimpleSchedule(x => x .WithIntervalInSeconds(10) .RepeatForever()) .Build(); // 告訴Quartz使用咱們的觸發器來安排做業 await scheduler.ScheduleJob(job, trigger); // 等待60秒 await Task.Delay(TimeSpan.FromSeconds(60)); // 關閉調度程序 await scheduler.Shutdown(); } catch (SchedulerException se) { await Console.Error.WriteLineAsync(se.ToString()); } } } }
HelloJob內容以下:asp.net
using Quartz; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Five.QuartzNetJob.ExecuteJobTask.Service { public class HelloJob : IJob { public Task Execute(IJobExecutionContext context) { Console.Out.WriteLineAsync("Greetings from HelloJob!"); return Task.CompletedTask; } } }
執行效果以下:異步
注:Quartz
的版本3.0.3中刪除了IJob
實現的異地調用,也就是不支持async
、await
異步調用,3.0.2版本支持異步調用。
2、觸發器類型
SimpleTrigger
的屬性包括:開始時間和結束時間,重複計數和重複間隔。重複計數能夠是零,一個正整數或常數值SimpleTrigger.RepeatIndefinitely
。重複時間間隔屬性必須是TimeSpan.Zero
或正的TimeSpan
值。請注意,重複間隔爲0會致使觸發器的「重複計數」觸發同時發生。
SimpleTrigger
實例使用TriggerBuilder
(用於觸發器的主屬性)和WithSimpleSchedule
擴展方法(用於SimpleTrigger
特定的屬性)構建。
在特定的時間內創建觸發器,無需重複,代碼以下:
// 觸發器構建器默認建立一個簡單的觸發器,實際上返回一個ITrigger ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartAt(DateTime.Now) //指定開始時間爲當前系統時間 .ForJob("job1", "group1") //經過JobKey識別做業 .Build();
在特定的時間創建觸發器,而後每十秒鐘重複十次:
// 觸發器構建器默認建立一個簡單的觸發器,實際上返回一個ITrigger ITrigger trigger = trigger = TriggerBuilder.Create() .WithIdentity("trigger2", "group2") .StartAt(DateTime.Now) // 指定開始時間 .WithSimpleSchedule(x => x .WithIntervalInSeconds(10) .WithRepeatCount(10)) // 請注意,重複10次將總共重複11次 .ForJob("job2", "group2") //經過JobKey識別做業 .Build();
構建一個觸發器,將在將來五分鐘內觸發一次:
// 觸發器構建器默認建立一個簡單的觸發器,實際上返回一個ITrigger ITrigger trigger = trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger3", "group3") .StartAt(DateBuilder.FutureDate(5, IntervalUnit.Minute)) //使用DateBuilder未來建立一個時間日期 .ForJob("job3", "group3") //經過JobKey識別做業 .Build();
創建一個如今當即觸發的觸發器,而後每隔五分鐘重複一次,直到22:00:
// 觸發器構建器默認建立一個簡單的觸發器,實際上返回一個ITrigger ITrigger trigger = trigger = TriggerBuilder.Create() .WithIdentity("trigger4", "group4") .WithSimpleSchedule(x => x .WithIntervalInMinutes(5)//每5秒執行一次 .RepeatForever()) .EndAt(DateBuilder.DateOf(22, 0, 0))//晚上22點結束 .Build();
創建一個觸發器,在一個小時後觸發,而後每2小時重複一次:
// 觸發器構建器默認建立一個簡單的觸發器,實際上返回一個ITrigger ITrigger trigger = TriggerBuilder.Create() .WithIdentity("trigger5") // 因爲未指定組,所以「trigger5」將位於默認分組中 .StartAt(DateBuilder.EvenHourDate(null)) // 獲取下個一小時時間 .WithSimpleSchedule(x => x .WithIntervalInHours(2)//執行間隔2小時 .RepeatForever()) .Build();
所以簡單的任務調度使用SimpleTrigger
徹底夠用,若是SimpleTrigger
仍是不能知足您的需求請往下看。
若是你須要一個基於相似日曆的概念而不是精確指定的SimpleTrigger
時間間隔的工做調度計劃,CronTriggers
一般比SimpleTrigger
更有用。
使用CronTrigger
,您能夠在每週一,週三的上午9點至上午10點之間指定開始時間表,例如「每星期五中午」或「每一個工做日和上午9點30分」,或者「每5分鐘」和星期五」。
即便如此,就像SimpleTrigger
同樣,CronTrigger
有一個startTime
,它指定了時間表的生效時間,還有一個(可選的)endTime
,用於指定應該中止時間表的時間。
這裏不在詳細介紹Cron
。
Cron表達式在線生成器:http://cron.qqe2.com/
Cron表達式詳細介紹:https://www.jianshu.com/p/e9ce1a7e1ed1
天天早上8點到下午5點創建一個觸發器,每隔一分鐘就會觸發一次:
// 觸發器構建器默認建立一個簡單的觸發器,實際上返回一個ITrigger ITrigger trigger = TriggerBuilder.Create() .WithIdentity("Job1", "group1") .WithCronSchedule("0 0/2 8-17 * * ?")//使用Cron表達式 .ForJob("Job1", "group1") .Build();
創建一個觸發器,天天在上午10:42開始執行:
// 觸發器構建器默認建立一個簡單的觸發器,實際上返回一個ITrigger ITrigger trigger = TriggerBuilder.Create() .WithIdentity("Job2", "group2") .WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(10, 42)) // 在這裏使用CronScheduleBuilder的靜態輔助方法 .ForJob("Job2", "group2") .Build();
構建一個觸發器,將在星期三上午10:42在除系統默認值以外的TimeZone中觸發:
// 觸發器構建器默認建立一個簡單的觸發器,實際上返回一個ITrigger ITrigger trigger = TriggerBuilder.Create() .WithIdentity("Job3", "group3") .WithCronSchedule("0 42 10 ? * WED", x => x .InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time"))) .ForJob("Job3", "group3") .Build();
總結: Quartz.NET
的3.0版本跟以前的版本api接口變化並不大。只是在3.0版本中添加了異步調用,並支持.net core。簡單的任務調度使用官網中的實例便可知足需求。
在網上找到不少關於asp.net 的任務管理,但沒有找到.net core 相關的後端任務管理。
我就根據官網還有網上dalao開源分享的項目實現了個簡單的後端任務。
項目地址:https://github.com/Potato-MC/QuartzNetJob
項目效果圖請看:http://www.cnblogs.com/miskis/p/8484252.html
項目使用asp.net core 2.0--->adminlte
-2.4.2-->SqlSugar
-4.6.4.3。
數據庫表可以使用SqlSugar
來生成。
public IActionResult Index() { //生成表 //var db = DataHelper.GetInstance(); //db.CodeFirst.InitTables(typeof(OperateLog), typeof(OperateLog)); return View(); }
結構以下:
Five.QuartzNetJob.DataService.DataHelper----------------------------------是ORM層,使用的是開源框架SqlSugar(官網地址:http://www.codeisbug.com/Doc/8)
Five.QuartzNetJob.DataService.Models---------------------------------------是實體類
Five.QuartzNetJob.ExecuteJobTask.Service---------------------------------IJob實現層
QuartzNet.Entity---------------------------------------------------------------------調度中心相關實體類
QuartzNet2.Core--------------------------------------------------------------------非.Net Core版本的調度管理中心,使用的是.net framework 4.6
Five.QuartzNetJob.Utils.Tool-----------------------------------------------------通用工具類庫
Five.QuartzNetJob.Web-----------------------------------------------------------後端
項目很簡單,就只實現了增刪改查。
統一管理任務調度,項目運行時開啓任務調度並執行已開啓的任務。
由於項目太過於簡單,就不在詳細介紹。
下面貼出任務調度中心代碼,歡迎各位dalao發表意見:
using QuartzNet.Entity; using Quartz; using Quartz.Impl; using System; using System.Collections.Specialized; using System.Threading.Tasks; using Five.QuartzNetJob.Utils.Tool; using System.Reflection; using System.Collections.Generic; namespace QuartzNet3.Core { /// <summary> /// 任務調度中心 /// </summary> public class SchedulerCenter { /// <summary> /// 任務調度對象 /// </summary> public static readonly SchedulerCenter Instance; static SchedulerCenter() { Instance = new SchedulerCenter(); } private Task<IScheduler> _scheduler; /// <summary> /// 返回任務計劃(調度器) /// </summary> /// <returns></returns> private Task<IScheduler> Scheduler { get { if (this._scheduler != null) { return this._scheduler; } // 從Factory中獲取Scheduler實例 NameValueCollection props = new NameValueCollection { { "quartz.serializer.type", "binary" }, //如下配置須要數據庫表配合使用,表結構sql地址:https://github.com/quartznet/quartznet/tree/master/database/tables //{ "quartz.jobStore.type","Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"}, //{ "quartz.jobStore.driverDelegateType","Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz"}, //{ "quartz.jobStore.tablePrefix","QRTZ_"}, //{ "quartz.jobStore.dataSource","myDS"}, //{ "quartz.dataSource.myDS.connectionString",AppSettingHelper.MysqlConnection},//鏈接字符串 //{ "quartz.dataSource.myDS.provider","MySql"}, //{ "quartz.jobStore.useProperties","true"} }; StdSchedulerFactory factory = new StdSchedulerFactory(props); return this._scheduler = factory.GetScheduler(); } } /// <summary> /// 運行指定的計劃(映射處理IJob實現類) /// </summary> /// <param name="jobGroup">任務分組</param> /// <param name="jobName">任務名稱</param> /// <returns></returns> public async Task<BaseQuartzNetResult> RunScheduleJob<T>(string jobGroup, string jobName) where T : ScheduleManage { BaseQuartzNetResult result; //開啓調度器 await this.Scheduler.Result.Start(); //建立指定泛型類型參數指定的類型實例 T t = Activator.CreateInstance<T>(); //獲取任務實例 ScheduleEntity scheduleModel = t.GetScheduleModel(jobGroup, jobName); //添加任務 var addResult = AddScheduleJob(scheduleModel).Result; if (addResult.Code == 1000) { scheduleModel.Status = EnumType.JobStatus.已啓用; t.UpdateScheduleStatus(scheduleModel); //用給定的密鑰恢復(取消暫停)IJobDetail await this.Scheduler.Result.ResumeJob(new JobKey(jobName, jobGroup)); result = new BaseQuartzNetResult { Code = 1000, Msg = "啓動成功" }; } else { result = new BaseQuartzNetResult { Code = -1 }; } return result; } /// <summary> /// 添加一個工做調度(映射程序集指定IJob實現類) /// </summary> /// <param name="m"></param> /// <returns></returns> private async Task<BaseQuartzNetResult> AddScheduleJob(ScheduleEntity m) { var result = new BaseQuartzNetResult(); try { //檢查任務是否已存在 var jk = new JobKey(m.JobName, m.JobGroup); if (await this.Scheduler.Result.CheckExists(jk)) { //刪除已經存在任務 await this.Scheduler.Result.DeleteJob(jk); } //反射獲取任務執行類 var jobType = FileHelper.GetAbsolutePath(m.AssemblyName, m.AssemblyName + "." + m.ClassName); // 定義這個工做,並將其綁定到咱們的IJob實現類 IJobDetail job = new JobDetailImpl(m.JobName, m.JobGroup, jobType); //IJobDetail job = JobBuilder.CreateForAsync<T>().WithIdentity(m.JobName, m.JobGroup).Build(); // 建立觸發器 ITrigger trigger; //校驗是否正確的執行週期表達式 if (!string.IsNullOrEmpty(m.Cron) && CronExpression.IsValidExpression(m.Cron)) { trigger = CreateCronTrigger(m); } else { trigger = CreateSimpleTrigger(m); } // 告訴Quartz使用咱們的觸發器來安排做業 await this.Scheduler.Result.ScheduleJob(job, trigger); result.Code = 1000; } catch (Exception ex) { await Console.Out.WriteLineAsync(string.Format("添加任務出錯{0}", ex.Message)); result.Code = 1001; result.Msg = ex.Message; } return result; } /// <summary> /// 運行指定的計劃(泛型指定IJob實現類) /// </summary> /// <param name="jobGroup">任務分組</param> /// <param name="jobName">任務名稱</param> /// <returns></returns> public async Task<BaseQuartzNetResult> RunScheduleJob<T, V>(string jobGroup, string jobName) where T : ScheduleManage, new() where V : IJob { BaseQuartzNetResult result; //開啓調度器 await this.Scheduler.Result.Start(); //建立指定泛型類型參數指定的類型實例 T t = Activator.CreateInstance<T>(); //獲取任務實例 ScheduleEntity scheduleModel = t.GetScheduleModel(jobGroup, jobName); //添加任務 var addResult = AddScheduleJob<V>(scheduleModel).Result; if (addResult.Code == 1000) { scheduleModel.Status = EnumType.JobStatus.已啓用; t.UpdateScheduleStatus(scheduleModel); //用給定的密鑰恢復(取消暫停)IJobDetail await this.Scheduler.Result.ResumeJob(new JobKey(jobName, jobGroup)); result = new BaseQuartzNetResult { Code = 1000, Msg = "啓動成功" }; } else { result = new BaseQuartzNetResult { Code = -1 }; } return result; } /// <summary> /// 添加任務調度(指定IJob實現類) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="m"></param> /// <returns></returns> private async Task<BaseQuartzNetResult> AddScheduleJob<T>(ScheduleEntity m) where T : IJob { var result = new BaseQuartzNetResult(); try { //檢查任務是否已存在 var jk = new JobKey(m.JobName, m.JobGroup); if (await this.Scheduler.Result.CheckExists(jk)) { //刪除已經存在任務 await this.Scheduler.Result.DeleteJob(jk); } //反射獲取任務執行類 // var jobType = FileHelper.GetAbsolutePath(m.AssemblyName, m.AssemblyName + "." + m.ClassName); // 定義這個工做,並將其綁定到咱們的IJob實現類 //IJobDetail job = new JobDetailImpl(m.JobName, m.JobGroup, jobType); IJobDetail job = JobBuilder.CreateForAsync<T>().WithIdentity(m.JobName, m.JobGroup).Build(); // 建立觸發器 ITrigger trigger; //校驗是否正確的執行週期表達式 if (!string.IsNullOrEmpty(m.Cron) && CronExpression.IsValidExpression(m.Cron)) { trigger = CreateCronTrigger(m); } else { trigger = CreateSimpleTrigger(m); } // 告訴Quartz使用咱們的觸發器來安排做業 await this.Scheduler.Result.ScheduleJob(job, trigger); result.Code = 1000; } catch (Exception ex) { await Console.Out.WriteLineAsync(string.Format("添加任務出錯", ex.Message)); result.Code = 1001; result.Msg = ex.Message; } return result; } /// <summary> /// 暫停指定的計劃 /// </summary> /// <param name="jobGroup">任務分組</param> /// <param name="jobName">任務名稱</param> /// <param name="isDelete">中止並刪除任務</param> /// <returns></returns> public BaseQuartzNetResult StopScheduleJob<T>(string jobGroup, string jobName, bool isDelete = false) where T : ScheduleManage, new() { BaseQuartzNetResult result; try { this.Scheduler.Result.PauseJob(new JobKey(jobName, jobGroup)); if (isDelete) { Activator.CreateInstance<T>().RemoveScheduleModel(jobGroup, jobName); } result = new BaseQuartzNetResult { Code = 1000, Msg = "中止任務計劃成功!" }; } catch (Exception ex) { result = new BaseQuartzNetResult { Code = -1, Msg = "中止任務計劃失敗" }; } return result; } /// <summary> /// 恢復運行暫停的任務 /// </summary> /// <param name="jobName">任務名稱</param> /// <param name="jobGroup">任務分組</param> public async void ResumeJob(string jobName, string jobGroup) { try { //檢查任務是否存在 var jk = new JobKey(jobName, jobGroup); if (await this.Scheduler.Result.CheckExists(jk)) { //任務已經存在則暫停任務 await this.Scheduler.Result.ResumeJob(jk); await Console.Out.WriteLineAsync(string.Format("任務「{0}」恢復運行", jobName)); } } catch (Exception ex) { await Console.Out.WriteLineAsync(string.Format("恢復任務失敗!{0}", ex)); } } /// <summary> /// 中止任務調度 /// </summary> public async void StopScheduleAsync() { try { //判斷調度是否已經關閉 if (!this.Scheduler.Result.IsShutdown) { //等待任務運行完成 await this.Scheduler.Result.Shutdown(); await Console.Out.WriteLineAsync("任務調度中止!"); } } catch (Exception ex) { await Console.Out.WriteLineAsync(string.Format("任務調度中止失敗!", ex)); } } /// <summary> /// 建立類型Simple的觸發器 /// </summary> /// <param name="m"></param> /// <returns></returns> private ITrigger CreateSimpleTrigger(ScheduleEntity m) { //做業觸發器 if (m.RunTimes > 0) { return TriggerBuilder.Create() .WithIdentity(m.JobName, m.JobGroup) .StartAt(m.BeginTime)//開始時間 .EndAt(m.EndTime)//結束數據 .WithSimpleSchedule(x => x .WithIntervalInSeconds(m.IntervalSecond)//執行時間間隔,單位秒 .WithRepeatCount(m.RunTimes))//執行次數、默認從0開始 .ForJob(m.JobName, m.JobGroup)//做業名稱 .Build(); } else { return TriggerBuilder.Create() .WithIdentity(m.JobName, m.JobGroup) .StartAt(m.BeginTime)//開始時間 .EndAt(m.EndTime)//結束數據 .WithSimpleSchedule(x => x .WithIntervalInSeconds(m.IntervalSecond)//執行時間間隔,單位秒 .RepeatForever())//無限循環 .ForJob(m.JobName, m.JobGroup)//做業名稱 .Build(); } } /// <summary> /// 建立類型Cron的觸發器 /// </summary> /// <param name="m"></param> /// <returns></returns> private ITrigger CreateCronTrigger(ScheduleEntity m) { // 做業觸發器 return TriggerBuilder.Create() .WithIdentity(m.JobName, m.JobGroup) .StartAt(m.BeginTime)//開始時間 .EndAt(m.EndTime)//結束時間 .WithCronSchedule(m.Cron)//指定cron表達式 .ForJob(m.JobName, m.JobGroup)//做業名稱 .Build(); } } }
總結:
開發已個小項目搞了很久才搞完,期間零零散散的開發,仍是太懶散了!
平時積累不夠,仍是太菜了!!!!!