Uwl.Admin.Core中使用QuartzNet定時任務子產品:
本文負責講解RabbitMQ的使用
Uwl.Admin.Core使用的技術有:
*、Async和Await 異步程式設計
*、Repository + Service 倉儲模式程式設計;倉儲模式支援工作單元
*、Swagger 前後端文檔說明,基于RESTful風格編寫接口
*、Cors 簡單的跨域解決方案
*、JWT自定義政策授權權限驗證
*、依賴注入選擇的是官方自帶的DI注入,沒有使用第三方架構,ORM使用EF Core,資料庫使用的是Sql server,(後期會擴充MySql版本);
*、AutoMapper 自動對象映射、
*、Linq To Sql \ lambda表達式樹查詢;(表達式樹查詢是個人擴充的,表達式樹的使用方法請參考Uwl.Data.Server.MenuServer的多條件查詢)
*、登入認證方式使用JWT認證方式,背景接口使用SwaggerUI展示,角色權限使用 自定義權限處理器PermissionHandler 繼承與微軟官方 IAuthorizationRequirement;
*、Excel導入導出使用的是Epplus第三方架構,導入導出隻需要配置Attribute特性就好,不需要在自己寫列名;導出隻支援List導出,暫時不支援Datatable;(Excel使用方法請參考UserController控制器)
*、Rabbit MQ消息隊列(目前暫無業務使用場景後期準備用來記錄日志)
*、Redis 輕量級分布式緩存;(Redis使用方法請參考Uwl.Data.Server.MenuServer類)
*、QuartzNet第三方任務架構;(使用方法請參考類庫Uwl.ScheduledTask.Job.TestJobOne類)
*、IdentityServer4授權模式已開發完成,未釋出示範伺服器代碼在github;(Identityserver4Auth分支)
Quartz.NET:
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
- 增加了
記憶體優化表和SQL Server
的初步支援Quartz.Impl.AdoJobStore.UpdateLockRowSemaphoreMOT
-
從相關性中删除Common.Logging
- 從
程序中删除的C5集合不再需要ILMerge
- 在插件啟動時添加對作業排程XML檔案的急切驗證的支援
- 在
中添加對額外的自定義時區解析器功能的支援TimeZoneUtil
變化
- 作業和插件現在位于獨立的程式集
包NuGet
Quartz.Jobs
中Quartz.Plugins
- ADO.NET提供者名稱已被簡化,提供者名稱沒有版本,例如
SqlServer-20 => SqlServer
- API方法已被重新使用,主要使用
,這隐藏了兩個IReadOnlyCollection
s和List小号HashSet
-
一直隐藏于内部(ILog等),就像它原本打算的那樣LibLog
-
消失了,舊的擁有的線程消失了SimpleThreadPool
- 排程程式方法已更改為基于任務,請記住等待它們
-
接口現在傳回一個任務IJob
- 一些
屬性已更改為IList
以正确反映意圖IReadOnlyList
-
支援已被删除SQL Server CE
-
現在将日期時間用于排除的日期,并具有DailyCalendar
接口來通路它們ISet
-
有新的方法,IObjectSerializer
,必須實作void Initialize()
-
取消了上下文的IInterruptableJob
CancellationToken
Quartz API的關鍵接口和類是:
-
- 與排程程式互動的主要API。IScheduler
-
- 您希望由排程程式執行的元件實作的接口。IJob
-
- 用于定義作業的執行個體。IJobDetail
-
- 定義執行給定Job的時間表的元件。ITrigger
-
- 用于定義/建構定義作業執行個體的JobBuilder
執行個體。JobDetail
-
- 用于定義/建構觸發器執行個體TriggerBuilder
一、Quartz.NET基本使用
1、建立Uwl.QuartzNet.JobCenter 類庫項目,使用NuGet添加Quartz,或使用程式包管理器引用,指令如下:
Install-Package Quartz
2、Uwl.QuartzNet.JobCenter 類庫的作用:
Uwl.QuartzNet.JobCenter 類庫是計劃任務管理中心,這裡我就放一段代碼了,不放太多,具體的實作可以下載下傳下來Uwl.Admin.Core項目看
/// <summary>
/// 開啟任務排程
/// </summary>
/// <returns></returns>
public async Task<JobResuleModel> StartScheduleAsync()
{
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<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());
}
}
}
}
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
特定的屬性)建構。
在特定的時間内建立觸發器,無需重複,代碼如下:
/// <summary>
/// 建立SimpleTrigger觸發器(簡單觸發器)
/// </summary>
/// <param name="sysSchedule"></param>
/// <param name="starRunTime"></param>
/// <param name="endRunTime"></param>
/// <returns></returns>
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
.RepeatForever()).ForJob(sysSchedule.Id.ToString(), sysSchedule.JobGroup).Build();
// 觸發作業立即運作,然後每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
/// <summary>
/// 建立類型Cron的觸發器
/// <param name="m"></param>
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;
/// <summary>
/// 實作接口Job
/// <param name="bundle"></param>
/// <param name="scheduler"></param>
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();
學不完的技術,寫不完的代碼。QQ群:773595012
▼-----------------------------------------▼-----------------------------------------▼