Quartz.NET是功能齊全的開源作業排程系統,可用於最小的應用程式到大型企業系統。
Quartz.NET具有三個主要概念:
- job:執行的後臺任務
- trigger:控制後臺任務執行的觸發器。
- scheduler:協調job和trigger
ASP.NET Core通過託管服務對執行“後臺任務”具有良好的支援,託管服務在ASP.NET Core應用程式啟動時啟動,並在應用程式生存期內在後臺執行,Quartz.NET版本3.2.0通過Quartz.Extensions.Hosting包引入了對該模式的直接支援,Quartz.Extensions.Hosting可以與ASP.NET Core應用程式一起使用,也可以與基於“通用主機”的工作程式服務一起使用。
雖然.NET Core可以建立“定時”後臺服務(例如,每10分鐘執行一次任務),但Quartz.NET提供了更為強大的解決方案, 通過使用Cron表示式,您可以確保任務在特定時間(例如,凌晨2:30)執行,或僅在特定的幾天執行,或這些時間的任意組合。Quartz.NET還允許您以叢集方式執行應用程式的多個例項,以便在任何時候都只能執行一個例項。
安裝Quartz.NET
Quartz.NET是一個.NET Standard 2.0 NuGet軟體包,所以大部分專案都是支援的,你可以執行安裝命令,dotnet add package Quartz.Extensions.Hosting
,或者在NNuget視覺化安裝,如果檢視該專案的.csproj,應該是下邊這樣:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<UserSecretsId>dotnet-QuartzWorkerService-9D4BFFBE-BE06-4490-AE8B-8AF1466778FD</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.2.3" />
</ItemGroup>
</Project>
安裝完成以後,這個包會自動安裝 Quartz.NET包,接下來,我們需要在我們的應用程式中註冊Quartz服務和Quartz 。
新增Quartz.NET hosted service
修改Program.cs,註冊服務
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
// Add the required Quartz.NET services
services.AddQuartz(q =>
{
// Use a Scoped container to create jobs. I'll touch on this later
q.UseMicrosoftDependencyInjectionScopedJobFactory();
});
// Add the Quartz.NET hosted service
services.AddQuartzHostedService(
q => q.WaitForJobsToComplete = true);
// other config
});
}
UseMicrosoftDependencyInjectionScopedJobFactory(),這個地方告訴Quartz.NET註冊一個IJobFactory,然後從DI容器中獲取Job,這樣也可以使用 Scoped 型別的服務。
WaitForJobsToComplete():當程式關閉時,此設定可確保Quartz.NET在退出之前等待Job正常結束。
如果現在執行您的應用程式,您將看到Quartz服務啟動,並將有很多日誌輸出到控制檯:
info: Quartz.Core.SchedulerSignalerImpl[0]
Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl
info: Quartz.Core.QuartzScheduler[0]
Quartz Scheduler v.3.2.3.0 created.
info: Quartz.Core.QuartzScheduler[0]
JobFactory set to: Quartz.Simpl.MicrosoftDependencyInjectionJobFactory
info: Quartz.Simpl.RAMJobStore[0]
RAMJobStore initialized.
info: Quartz.Core.QuartzScheduler[0]
Scheduler meta-data: Quartz Scheduler (v3.2.3.0) 'QuartzScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'Quartz.Simpl.DefaultThreadPool' - with 10 threads.
Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered.
info: Quartz.Impl.StdSchedulerFactory[0]
Quartz scheduler 'QuartzScheduler' initialized
info: Quartz.Impl.StdSchedulerFactory[0]
Quartz scheduler version: 3.2.3.0
info: Quartz.Core.QuartzScheduler[0]
Scheduler QuartzScheduler_$_NON_CLUSTERED started.
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
...
現在,您已經將Quartz作為託管服務執行在您的應用程式中,但是現在還沒有新增需要執行的Job。
建立一個IJob
這個地方我建立一個簡單的服務,並且我可以從建構函式中獲取服務。
using Microsoft.Extensions.Logging;
using Quartz;
using System.Threading.Tasks;
[DisallowConcurrentExecution]
public class HelloWorldJob : IJob
{
private readonly ILogger<HelloWorldJob> _logger;
public HelloWorldJob(ILogger<HelloWorldJob> logger)
{
_logger = logger;
}
public Task Execute(IJobExecutionContext context)
{
_logger.LogInformation("Hello world!");
return Task.CompletedTask;
}
}
我還用[DisallowConcurrentExecution]特性,防止Quartz.NET嘗試同時執行同一個作業。
設定Job
這個地方通常使用Cron表示式,來設定job的執行時間。
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionScopedJobFactory();
// Create a "key" for the job
var jobKey = new JobKey("HelloWorldJob");
// Register the job with the DI container
q.AddJob<HelloWorldJob>(opts => opts.WithIdentity(jobKey));
// Create a trigger for the job
q.AddTrigger(opts => opts
.ForJob(jobKey) // link to the HelloWorldJob
.WithIdentity("HelloWorldJob-trigger") // give the trigger a unique name
.WithCronSchedule("0/5 * * * * ?")); // run every 5 seconds
});
services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
// ...
});
現在執行應用程式,您將看到和以前相同的啟動訊息,然後每隔5秒鐘就會看到HelloWorldJob寫入控制檯的資訊:
將配置提取到appsettings.json
一般情況,我們都不會把cron表示式寫死在程式碼中,一般是設定在appsettings.json中
{
"Quartz": {
"HelloWorldJob": "0/5 * * * * ?"
}
}
為了更簡單的註冊服務,這個地方我簡單做了一個封裝,這樣也更靈活。
public static class ServiceCollectionQuartzConfiguratorExtensions
{
public static void AddJobAndTrigger<T>(
this IServiceCollectionQuartzConfigurator quartz,
IConfiguration config)
where T : IJob
{
// Use the name of the IJob as the appsettings.json key
string jobName = typeof(T).Name;
// Try and load the schedule from configuration
var configKey = $"Quartz:{jobName}";
var cronSchedule = config[configKey];
// Some minor validation
if (string.IsNullOrEmpty(cronSchedule))
{
throw new Exception($"No Quartz.NET Cron schedule found for job in configuration at {configKey}");
}
// register the job as before
var jobKey = new JobKey(jobName);
quartz.AddJob<T>(opts => opts.WithIdentity(jobKey));
quartz.AddTrigger(opts => opts
.ForJob(jobKey)
.WithIdentity(jobName + "-trigger")
.WithCronSchedule(cronSchedule)); // use the schedule from configuration
}
}
然後修改Program.cs,然後使用擴充套件方法:
public class Program
{
public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionScopedJobFactory();
// Register the job, loading the schedule from configuration
q.AddJobAndTrigger<HelloWorldJob>(hostContext.Configuration);
});
services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
});
}
再次執行該應用程式將提供相同的輸出:Job每5秒輸入一次資訊。
原文作者: andrewlock
原文連結: https://andrewlock.net/using-quartz-net-with-asp-net-core-and-worker-services/
最後
歡迎掃碼關注我們的公眾號 【全球技術精選】,專注國外優秀部落格的翻譯和開源專案分享,也可以新增QQ群 897216102