Uwl.Admin.Core中使用:
本文負責講解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
從相關性中刪除- 從
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項目看
/// <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
{
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
/// <summary>
/// 創建類型Cron的觸發器
/// </summary>
/// <param name="m"></param>
/// <returns></returns>
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方法;
具體代碼實現
/// <summary>
/// 注入反射獲取依賴對象
/// </summary>
private readonly IServiceProvider _serviceProvider;
public IOCJobFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
/// <summary>
/// 實現接口Job
/// </summary>
/// <param name="bundle"></param>
/// <param name="scheduler"></param>
/// <returns></returns>
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();
}
}