Merry Christmas【壹起学】系列旨在向广大小伙伴推荐优秀开源框架,如果你有比较好的成型的NetCore 开源作品,欢迎给我联系,我们一起学!
本期项目地址:https://github.com/GeorGeWzw/Uwl.Admin.Core
Quartz.NET官网地址:https://www.quartz-scheduler.net/
Quartz.NET文档地址:https://www.quartz-scheduler.net/documentation/index.html
Quartz.NET
是一个开源的作业调度框架,是OpenSymphony
的 Quartz API
的.NET移植,它用C#写成,可用于winform
和asp.net
应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,支持cron-like
表达式等等。
现在Quartz.NET3.0
已支持Asp.Net Core
,3.0新功能如下:
新功能
具有异步/等待支持的基于任务的作业,内部以异步/等待方式工作
支持.NET Core / netstandard 2.0和.NET Framework 4.5.2及更高版本
通过提供程序名称
SQLite-Microsoft
支持Microsoft.Data.Sqlite
,旧的提供程序SQLite
也仍然有效增加了
SQLServer
内存优化表和Quartz.Impl.AdoJobStore.UpdateLockRowSemaphoreMOT
的初步支持Common.Logging
从相关性中删除从
ILMerge
进程中删除的C5集合不再需要在插件启动时添加对作业调度XML文件的急切验证的支持
在
TimeZoneUtil
中添加对额外的自定义时区解析器功能的支持
变化
作业和插件现在位于独立的程序集
NuGet
包Quartz.Jobs
和Quartz.Plugins
中ADO.NET提供者名称已被简化,提供者名称没有版本,例如
SqlServer-20 => SqlServer
API方法已被重新使用,主要使用
IReadOnlyCollection
,这隐藏了两个HashSet
s和List小号LibLog
一直隐藏于内部(ILog等),就像它原本打算的那样SimpleThreadPool
消失了,旧的拥有的线程消失了调度程序方法已更改为基于任务,请记住等待它们
IJob
接口现在返回一个任务一些
IList
属性已更改为IReadOnlyList
以正确反映意图SQL Server CE
支持已被删除DailyCalendar
现在将日期时间用于排除的日期,并具有ISet
接口来访问它们IObjectSerializer
有新的方法,void Initialize()
,必须实现IInterruptableJob
取消了上下文的CancellationToken
Quartz API的关键接口和类是:
IScheduler
- 与调度程序交互的主要API。IJob
- 您希望由调度程序执行的组件实现的接口。IJobDetail
- 用于定义作业的实例。ITrigger
- 定义执行给定Job的时间表的组件。JobBuilder
- 用于定义/构建定义作业实例的JobDetail
实例。TriggerBuilder
- 用于定义/构建触发器实例
一、Quartz.NET基本使用
1、新建Uwl.QuartzNet.JobCenter 类库项目,使用NuGet添加Quartz,或使用程序包管理器引用,命令如下:
Install-Package Quartz
2、Uwl.QuartzNet.JobCenter 类库的作用:
Uwl.QuartzNet.JobCenter 类库是计划任务管理中心,这里我就放一段代码了,不放太多,具体的实现可以下载下来Uwl.Admin.Core项目看
/// /// 开启任务调度 /// /// public async TaskStartScheduleAsync() { var result = new JobResuleModel(); try { if (!this._scheduler.Result.IsStarted) { //等待任务运行完成 await this._scheduler.Result.Start(); await Console.Out.WriteLineAsync("任务调度开启!"); result.IsSuccess = true; result.Message = $"任务调度开启成功"; return result; } else { result.IsSuccess = false; result.Message = $"任务调度已经开启"; return result; } } catch (Exception) { throw; } }
如果你想添加JSON序列化,只需要以同样的方式添加Quartz.Serialization.Json
包。
2、简单实例,代码如下:
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() .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()); } } }}
TestJobOne内容如下:
using Quartz;using System;using System.Diagnostics;using System.Threading.Tasks;using Uwl.Common.Cache.RedisCache;using Uwl.Common.Subscription;using Uwl.Data.Server.MenuServices;namespace Uwl.ScheduledTask.Job{ public class TestJobOne : IJob { private readonly IRedisCacheManager _redisCacheManager; private readonly IMenuServer _menuServer; public TestJobOne(IRedisCacheManager redisCacheManager,IMenuServer menuServer) { this._redisCacheManager = redisCacheManager; this._menuServer = menuServer; } public async Task Execute(IJobExecutionContext context) { //记录Job时间 Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); await Console.Out.WriteLineAsync("我是有Redis的注入测试任务"); var list = await _menuServer.GetMenuList(); await Console.Out.WriteLineAsync("菜单表里总数量" + list.Count.ToString()); stopwatch.Stop(); await Console.Out.WriteLineAsync("执行时间" + stopwatch.Elapsed.TotalMilliseconds); //if (stopwatch.Elapsed.TotalMilliseconds > 0) //{ // //写入日志性能监控表和执行是否出错 //} } }}
执行效果:
二、触发器类型
1、SimpleTrigger触发器(简单触发器)
SimpleTrigger
的属性包括:开始时间和结束时间,重复计数和重复间隔。重复计数可以是零,一个正整数或常数值SimpleTrigger.RepeatIndefinitely
。重复时间间隔属性必须是TimeSpan.Zero
或正的TimeSpan
值。请注意,重复间隔为0会导致触发器的“重复计数”触发同时发生。SimpleTrigger
实例使用TriggerBuilder
(用于触发器的主属性)和WithSimpleSchedule
扩展方法(用于SimpleTrigger
特定的属性)构建。
在特定的时间内建立触发器,无需重复,代码如下:
/// /// 创建SimpleTrigger触发器(简单触发器) /// /// /// /// /// private ITrigger CreateSimpleTrigger(SysSchedule sysSchedule) { if(sysSchedule.RunTimes>0) { ITrigger trigger = TriggerBuilder.Create() .WithIdentity(sysSchedule.Id.ToString(), sysSchedule.JobGroup) .StartAt(sysSchedule.BeginTime.Value) .EndAt(sysSchedule.EndTime.Value) .WithSimpleSchedule(x => x.WithIntervalInSeconds(sysSchedule.IntervalSecond) .WithRepeatCount(sysSchedule.RunTimes)).ForJob(sysSchedule.Id.ToString(), sysSchedule.JobGroup).Build(); return trigger; } else { ITrigger trigger = TriggerBuilder.Create() .WithIdentity(sysSchedule.Id.ToString(), sysSchedule.JobGroup) .StartAt(sysSchedule.BeginTime.Value) .EndAt(sysSchedule.EndTime.Value) .WithSimpleSchedule(x => x.WithIntervalInSeconds(sysSchedule.IntervalSecond) .RepeatForever()).ForJob(sysSchedule.Id.ToString(), sysSchedule.JobGroup).Build(); return trigger; } // 触发作业立即运行,然后每10秒重复一次,无限循环 }
因此简单的任务调度使用SimpleTrigger
完全够用,如果SimpleTrigger
还是不能满足您的需求请往下看。
2、CronTrigger触发器
如果你需要一个基于类似日历的概念而不是精确指定的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
/// /// 创建类型Cron的触发器 /// /// /// private ITrigger CreateCronTrigger(SysSchedule sysSchedule) { // 作业触发器 return TriggerBuilder.Create() .WithIdentity(sysSchedule.Id.ToString(), sysSchedule.JobGroup) .StartAt(sysSchedule.BeginTime.Value)//开始时间 .EndAt(sysSchedule.EndTime.Value)//结束数据 .WithCronSchedule(sysSchedule.Cron)//指定cron表达式 .ForJob(sysSchedule.Id.ToString(), sysSchedule.JobGroup)//作业名称 .Build(); }
三、在Uwl.Admin.Core配置使用方法
1、在Uwl.ScheduledTask.Job类库下面新建一个类继承于JobBase和IJob接口:
2、在新建的类里面写一个方法,并且把这个方法通过实现的IJob的Execute方法传给JobBase基类:
3、在新建的类里面写一个方法,并且把这个方法通过实现的IJob的Execute方法传给JobBase基类:
在uwl.admin后台管理的定时任务模块添加一个新的任务,填写对应的名称,这里需要注意的是(DLL程序集是☞你的类库,任务所在类是指你的Job需要执行的Calss,这里有两种触发类型,一个是simple类型,一个是Cron类型可以根据自己的需要去设置对应的类型
simple类型适合简单任务,开始时间和结束时间非必填,不填的话在你点击开始任务的时候就是默认执行,结束时间取的是最大时间)
为什么要填程序集和类的名字呢,因为这里我是通过反射来获取程序集和类来进行执行那个Job的
我们把这些配置完成之后点击启动任务就OK啦~~
这里还有一点小问题……就是程序暂停运行了之后不会自动启动在执行的任务,后面我会慢慢修复,暂且各位大佬每次发布之后记得点击一下启动任务嗷~~~
总结(很重要): Quartz.NET
的3.0版本跟之前的版本api接口变化并不大。只是在3.0.7版本中添加了异步调用,并支持.net core。简单的任务调度使用官网中的实例即可满足需求,进行依赖注入的时候应当重写IJobFactory工厂,在IJobFactory工厂内重写 NewJob,ReturnJob方法;
具体代码实现
/// /// 注入反射获取依赖对象 /// private readonly IServiceProvider _serviceProvider; public IOCJobFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } /// /// 实现接口Job /// /// /// /// public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { try { // 这个位置需要重新创建_serviceProvider.CreateScope();容器,不然会提示找不到Job/还有一种情况是你也注入了但是Job无法执行,所以这个位置应当重新创建容器实例, var serviceScope = _serviceProvider.CreateScope(); var job = serviceScope.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob; return job; //var job = _serviceProvider.GetService(bundle.JobDetail.JobType) as IJob; //return job; } catch (Exception e) { throw e; } } public void ReturnJob(IJob job) { var disposable = job as IDisposable; if(disposable!=null) { disposable.Dispose(); } }
?点击查看原文