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.TaskScheduler
的Worker Service
(輔助角色服務)項目
創建Theo.TaskScheduler
項目,用於作業調度
添加引用
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
文件夾,文件夾下新建JobModel
、JobGroupModel
和ScheduleModel
三個類。
* JobModel:計划任務job模型
* JobGroupModel:計划任務job組模型
* ScheduleModel:作業調度模型
新建IOCHelper
文件,注入業務代碼(IProviderDemo),並暴露IServiceProvider
。
新建Quartz
文件夾,文件夾下新建:
- QuartzJob:實現
Quartz.IJob
的Execute
方法,來調用配置的作業調度。因為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
並重載StopAsync
和ExecuteAsync
方法。完成讀取作業調度配置文件並交給Quartz.Net
組件的工作。通過appsetting.json
文件Quartz:Config
配置找到作業調度配置文件路徑,讀取配置文件。配置文件支持xml
、json
兩種格式二選一。根據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源碼