Uwl.Admin.Core開源框架(二) 使用QuartzNet


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是一個開源的作業調度框架,是OpenSymphonyQuartz API的.NET移植,它用C#寫成,可用於winformasp.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中添加對額外的自定義時區解析器功能的支持

變化

  • 作業和插件現在位於獨立的程序集NuGetQuartz.JobsQuartz.Plugins
  • ADO.NET提供者名稱已被簡化,提供者名稱沒有版本,例如SqlServer-20 => SqlServer
  • API方法已被重新使用,主要使用IReadOnlyCollection,這隱藏了兩個HashSets和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();
            }
            
        }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM