天天看點

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

是一個開源的作業排程架構,是

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)
            //{
            //    //寫入日志性能監控表和執行是否出錯
            //}
        }
    }
}

      

執行效果:

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

二、觸發器類型

 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接口:

  

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

  2、在建立的類裡面寫一個方法,并且把這個方法通過實作的IJob的Execute方法傳給JobBase基類:

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

  3、在建立的類裡面寫一個方法,并且把這個方法通過實作的IJob的Execute方法傳給JobBase基類:

  在uwl.admin背景管理的定時任務子產品添加一個新的任務,填寫對應的名稱,這裡需要注意的是(DLL程式集是☞你的類庫,任務所在類是指你的Job需要執行的Calss,這裡有兩種觸發類型,一個是simple類型,一個是Cron類型可以根據自己的需要去設定對應的類型

  simple類型适合簡單任務,開始時間和結束時間非必填,不填的話在你點選開始任務的時候就是預設執行,結束時間取的是最大時間)

  為什麼要填程式集和類的名字呢,因為這裡我是通過反射來擷取程式集和類來進行執行那個Job的

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

  我們把這些配置完成之後點選啟動任務就OK啦~~

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

  這裡還有一點小問題……就是程式暫停運作了之後不會自動啟動在執行的任務,後面我會慢慢修複,暫且各位大佬每次釋出之後記得點選一下啟動任務嗷~~~

  總結(很重要): 

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

▼-----------------------------------------▼-----------------------------------------▼

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