Quartz.Net 組件的封裝使用Quartz.AspNetCore


Quartz.Net 組件的封裝使用

Quartz.Net是面向.NET的一款功能齊全的開源作業調度組件,你可以把它嵌入你的系統中實現作業調度,也可以基於Quartz.Net開發一套完整的作業調度系統。它既支持簡單的timer,也支持靈活強大的corn表達式。本文提供了一種把Quartz.Net嵌入項目中的實現。你可以看到系統其它項目用於作業調度的項目解耦,這樣做可以實現一次封裝,多系統使用

1、環境

  • 操作系統:Windows 10
  • IDE:Visual Studio 2019-16.8.3
  • .Net Core版本:.NET 5.0

2、創建項目,添加引用

創建一個空解決方案Theo.QuartzDemo,然后開始添加新項目。

2.1、創建名為Theo.Business的類庫項目

創建.Net Core類庫項目Theo.Business,代表系統的業務項目。添加Microsoft.Extensions.Logging引用。
添加IProviderDemo接口,定義兩個方法分別提供發郵件和發短信服務。你可能覺得發郵件和發短信服務不應該在同一個provider中,但是作為示例,我偷了個懶。
IProviderDemo.cs:

using System;

namespace Theo.Business
{
    public interface IProviderDemo
    {

        /// <summary>
        /// 模擬提供發郵件服務
        /// </summary>
        /// <param name="param">參數</param>
        void AutoSendMail(string param);

        /// <summary>
        /// 模擬提供發短信服務
        /// </summary>
        /// <param name="param">參數</param>
        void AutoSendSMS(string param);
    }
}

添加IProviderDemo的實現:ProviderDemo

using Microsoft.Extensions.Logging;
using System;

namespace Theo.Business
{
    ///<summary>
    /// 模擬業務邏輯
    ///</summary>
    public class ProviderDemo : IProviderDemo
    {
        private readonly ILogger<ProviderDemo> _logger;

        /// <summary>
        /// .ctor
        /// </summary>
        /// <param name="logger"></param>
        public ProviderDemo(ILogger<ProviderDemo> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 模擬提供發郵件服務
        /// </summary>
        /// <param name="param">參數</param>
        public void AutoSendMail(string param)
        {
            _logger.LogError($"[{DateTimeOffset.Now:HH:mm:ss.fff}]\t{nameof(AutoSendMail)}\tparam:{param}");
        }

        /// <summary>
        /// 模擬提供發短信服務
        /// </summary>
        /// <param name="param">參數</param>
        public void AutoSendSMS(string param)
        {
            _logger.LogInformation($"[{DateTimeOffset.Now:HH:mm:ss.fff}]\t{nameof(AutoSendSMS)}\tparam:{param}");
        }
    }
}

2.2、添加名為Theo.TaskSchedulerWorker Service(輔助角色服務)項目

創建Theo.TaskScheduler項目,用於作業調度
創建Worker Service項目
添加引用

Install-Package Microsoft.Extensions.Hosting.Systemd
Install-Package Microsoft.Extensions.Hosting.WindowsServices
Install-Package Quartz.AspNetCore
Install-Package Microsoft.Extensions.Logging.Log4Net.AspNetCore
Install-Package System.Text.Json
  • Microsoft.Extensions.Hosting.Systemd:用以支持Linux守護進程,部署在Linux平台時需要此組件
  • Microsoft.Extensions.Hosting.WindowsServices:用以支持Windows服務,部署在Windows平台時需要此組件
  • Quartz.AspNetCore:.Net Core平台的Quartz組件
  • Microsoft.Extensions.Logging.Log4Net.AspNetCore:Log4Net日志組件,當然也可以是Nlog/Serilog等其他任意組件
  • 添加對Theo.Business項目的引用

3、封裝Quartz.Net

Theo.TaskScheduler項目中封裝Quartz.Net。封裝的主要目的是為了更方便的配置作業調度計划。實現過程中的工作量主要在讀取配置並交給Quartz.Net組件,以告訴組件如何根據配置去調用指定方法。

新建Models文件夾,文件夾下新建JobModelJobGroupModelScheduleModel三個類。
* JobModel:計划任務job模型
* JobGroupModel:計划任務job組模型
* ScheduleModel:作業調度模型
新建IOCHelper文件,注入業務代碼(IProviderDemo),並暴露IServiceProvider
新建Quartz文件夾,文件夾下新建:

  • QuartzJob:實現Quartz.IJobExecute方法,來調用配置的作業調度。因為Quartz.AspNetCore目前對IOC的支持有限,這里實現一個protected void Init(ILogger logger, IServiceProvider serviceProvider)方法,以便在Execute方法中記錄日志以及通過IServiceProvider發現服務。Execute方法的邏輯:
    • IJobExecutionContext讀取配置
    • 通過IServiceProvider發現服務
    • Invoke調用
  • BlockedJob:繼承QuartzJob,類添加[DisallowConcurrentExecution]屬性,指定為阻塞模式job。例如某個job調用頻率是每分鍾1次,本次調用時發現上次調用還未執行結束,將放棄本次調用且不會拋出任何異常
  • ConcurrentJob:繼承QuartzJob,可並發執行job,不管上次執行是否結束,到時間就重新調用執行
    新建QuartzWorker類,繼承BackgroundService並重載StopAsyncExecuteAsync方法。完成讀取作業調度配置文件並交給Quartz.Net組件的工作。通過appsetting.json文件Quartz:Config配置找到作業調度配置文件路徑,讀取配置文件。配置文件支持xmljson兩種格式二選一。根據Quartz:Watch配置確定是否監控配置文件變化。如果Quartz:Watch配置為true,在服務運行過程中可以通過修改作業調度配置文件來修改作業調度計划。
    新建Configs文件夾,文件夾下新建SchedulerConfig.json或者SchedulerConfig.xml配置文件,用以配置作業調度計划。SchedulerConfig.json配置示例如下,發郵件服務調用頻率為從5秒開始每10秒鍾一次,發短信服務調用頻率為從0秒開始每10秒鍾一次
{
  "GroupList": [
    {
      "Name": "JobGroup",
      "JobList": [
        {
          "Disabled": false,
          "Desc": "發郵件服務",
          "Name": "SendMail",
          "Cron": "5/10 * * * * ? *",
          "DllName": "Theo.Business",
          "ClassName": "Theo.Business.IProviderDemo",
          "MethodName": "AutoSendMail"
        },
        {
          "Disabled": false,
          "Desc": "發短信服務",
          "Name": "SendSMS",
          "Cron": "0/10 * * * * ? *",
          "DllName": "Theo.Business",
          "ClassName": "Theo.Business.IProviderDemo",
          "MethodName": "AutoSendSMS"
        }
      ]
    }
  ]
}

appsetting.json添加配置,指定作業調度配置文件:

...
  "Quartz": {
    "Config": "Configs/SchedulerConfig.json",
    "Watch": true
  }
...

4、配置Program並測試運行

Program.cs文件:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using Theo.TaskScheduler.Quartz;

namespace Theo.TaskScheduler
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseWindowsService()   //支持Windows服務,  其他平台自動忽略
                .UseSystemd()          //支持Linux守護進程,其他平台自動忽略
                //使用log4net日志組件,並指定配置文件
                .ConfigureLogging(conf => conf.AddLog4Net("Configs/log4net.config"))
                .ConfigureAppConfiguration((hostContext, config) =>
                {
                    config.SetBasePath(AppContext.BaseDirectory);
                    var env = hostContext.HostingEnvironment;
                    if (env.IsDevelopment())
                    {
                        config.AddJsonFile("appsettings.Development.json");
                    }
                    else
                    {
                        config.AddJsonFile("appsettings.json");
                    }
                })
                .ConfigureServices((hostContext, services) =>
                {
                    IOCHelper.InjectDependencies(services);
                    services.AddHostedService<QuartzWorker>();
                });
    }
}

啟動調試之前還需要為Log4Net日志組件添加配置文件。根據Program.cs中指定的配置文件Configs/log4net.config,我們在項目Configs文件夾下添加文件log4net.config。具體的配置規則您可以去 log4net官網 查找,篇幅所限,本文不做說明。

終於可以F5啟動調試,結果如下圖所示。
我們的作業調度配置:SendMail從5秒開始每10秒鍾一次SendSMS從0秒開始每10秒鍾一次。從下圖可以看出,作業調度運行情況和我們預期的一致。
調試運行結果
在運行中,我們把SendMail作業改為從1秒開始每10秒鍾一次,把SendSMS作業禁用,然后新增一個名為SendSMS-1的作業,調用頻率為從0秒開始每3秒鍾一次。從下圖可以看出(``開始),程序檢測到了配置文件的變化,並啟用的新的作業調度計划。
在這里插入圖片描述

5、部署服務

部署到Linux平台的方法,您可以參閱官方文檔:
若要部署到Windows平台,您可以發布Theo.TaskScheduler項目,然后通過執行shell命令或者把命令包裝成.bat文件並執行的方式安裝為Windows服務並啟動。參考命令如下:

@echo.服務啟動......  
@echo off  
@sc create TheoTaskScheduler binPath= "publish\Theo.TaskScheduler.exe"
@sc description TheoTaskScheduler "TheoDemo-任務調度服務"
@net start TheoTaskScheduler  
@echo off
@echo.啟動完畢!  
@pause

6、總結

本文介紹了Quartz.Net組件在DotNetCore平台封裝使用的詳細步驟。
Quartz.AspNetCore封裝到了單獨DotNetCore項目中,與其他業務代碼解耦。並實現了作業調度計划的配置化,和服務運行中實時監控配置文件功能。可以在運行中動態禁用/啟用作業,添加新的作業,修改現有作業的調度計划(cron表達式)。
上文有提到示例中實現了可以指定是否允許並行調用作業的功能(BlockedJob/ConcurrentJob),但因篇幅所限並未貼出測試結果。
展望:

1、在大型項目中,調度服務可能以集群或者分布式架構的形式運行。這就要考慮把作業調度計划配置到數據庫或者redis等緩存中。服務啟動時候從數據庫或者緩存中加載作業調度計划,並訂閱修改作業調度計划事件,以便在調度計划發生改變時及時更新。
2、本例僅實現了調度服務運行中實時禁用/啟用作業,添加新的作業,修改現有作業的調度計划的功能,以滿足基本使用需求。如果您有興趣/需求,可以自行探索如何實時修改是否允許並行調用作業等功能。

示例代碼已上傳至資源庫,僅需5積分,感謝您的支持:Theo.QuartzDemo源碼


免責聲明!

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



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