Quartz.NET這麼NB的做業調度系統,不會還行?html
今天介紹一下Quartz.NET的託管運行,官網傳送門。git
Quartz.NET,按官網上的說法,是一款功能齊全的任務調度系統,從小型應用到大型企業級系統都能適用。在衆多項目中,Quartz.NET以可靠、集羣的方式,被用做在定時器上運行後臺任務的一種方式。github
Quartz.NET主要完成兩個方面的內容:c#
ASP.NET Core自己對於經過託管服務運行後臺任務就支持的很好。當ASP.NET啓動託管服務時,應用程序啓動,並在生命週期內在後臺運行。經過建立Quartz.NET託管服務,可使用標準的.Net Core託管服務,在後臺運行任務。安全
Quartz.NET能夠建立定時的任務,例如每十分鐘運行一個任務。除此以外,Quartz.NET還能夠經過Cron觸發器,定義任務在特定的日子或特定的時間運行,例如天天凌晨兩點執行一個任務。它還容許以集羣的方式運行應用程序的多個實例,以便在任什麼時候間確保只有一個實例運行給定的任務。bash
下面,就針對這些特性和功能,進行詳細的說明。微信
爲防止非受權轉發,這兒給出本文的原文連接:http://www.javashuo.com/article/p-uqtgnnvo-nv.htmlapp
Quartz.NET提供了NuGet包,因此安裝很簡單:框架
% dotnet add package quartz
這是個司機就知道,不詳說了。async
看一下安裝後的.csproj
文件內容:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="quartz" Version="3.2.2" />
</ItemGroup>
</Project>
咱們用個例子來講明 - 建立一個Demo
的實現。它將寫入ILogger<>
。咱們會使用Quartz.NET的接口IJob
來實現,並使用依賴注入將日誌注入到構造函數中。
[DisallowConcurrentExecution]
public class DemoJob : IJob
{
private readonly ILogger<DemoJob> _logger;
public DemoJob(ILogger<DemoJob> logger)
{
_logger = logger;
}
public Task Execute(IJobExecutionContext context)
{
_logger.LogInformation("Demo !");
return Task.CompletedTask;
}
}
在類的前面,我用了一個DisallowConcurrentExecution
屬性。這個屬性能夠防止Quartz.NET同時運行相同的做業。
一般狀況下,Quartz.NET會使用Activator.CreateInstance
來建立做業的實例。
在咱們這個例子裏,咱們換一種方式。使用IJobFactory
實現,並與ASP.NET Core的依賴注入容器掛鉤。
public class SingletonJobFactory : IJobFactory
{
private readonly IServiceProvider _serviceProvider;
public SingletonJobFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
}
public void ReturnJob(IJob job)
{
}
}
這個IJobFactory
的實現,在構造函數中引入IServiceProvider
,並實現接口。
接口中,最重要的是NewJob()
方法。這個方法須要返回Quartz.NET調度器請求的IJob
。在咱們的例子中,咱們直接委託給IServiceProvider
,並讓DI容器找到所需的實例。
ReturnJob()
方法是調度程序返回和銷燬IJobFactory
建立的做業的地方。不過,由於咱們使用了IServiceProvider
,而它沒有提供這樣的處理。因此,從安全的角度,應該使用單例做業。
在第三節中,咱們建立了一個IJob
的實現。這個實現直接使用就能夠。
可是,咱們這兒要加點內容。咱們把Quartz
的託管服務作成一個通用實現,來調度任意的做業。所以,咱們建立一個簡單的DTO,並使用它來定義一個給定做業類型的時間器調度:
public class JobSchedule
{
public JobSchedule(Type jobType, string cronExpression)
{
JobType = jobType;
CronExpression = cronExpression;
}
public Type JobType { get; }
public string CronExpression { get; }
}
在這個類中,JobType
是一個做業的類型,例如本例子中的DemoJob
。CronExpression
是一個Cron
的表達式。
Cron表達式容許複雜的計時器調度,因此能夠設置規則,好比每月的5號和20號,在早上8點到10點之間每半小時觸發一次。
關於Quartz.NET
的Cron
表達式,能夠在這兒查到。
注意:不一樣操做系統使用的Cron表達式有必定的區別,寫表達式的時候必定要注意這一點。
而後,咱們把做業添加到Startup.ConfigureServices()
中:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<IJobFactory, SingletonJobFactory>();
services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
services.AddSingleton<DemoJob>();
services.AddSingleton(new JobSchedule(
jobType: typeof(DemoJob),
cronExpression: "0/5 * * * * ?")); // 每5秒運行一次
}
這段代碼向DI添加了四個單例對象:
如今,主要代碼已經完成,就差Quartz
託管服務了。
QuartzHostedService
是本身建立的Quartz
託管服務,繼承於IHostedService
,用於設置Quartz
調度器,並在後臺啓動它。
先看一下完整的代碼:
public class QuartzHostedService : IHostedService
{
private readonly ISchedulerFactory _schedulerFactory;
private readonly IJobFactory _jobFactory;
private readonly IEnumerable<JobSchedule> _jobSchedules;
public QuartzHostedService(ISchedulerFactory schedulerFactory, IJobFactory jobFactory, IEnumerable<JobSchedule> jobSchedules)
{
_schedulerFactory = schedulerFactory;
_jobSchedules = jobSchedules;
_jobFactory = jobFactory;
}
public IScheduler Scheduler { get; set; }
public async Task StartAsync(CancellationToken cancellationToken)
{
Scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
Scheduler.JobFactory = _jobFactory;
foreach (var jobSchedule in _jobSchedules)
{
var job = CreateJob(jobSchedule);
var trigger = CreateTrigger(jobSchedule);
await Scheduler.ScheduleJob(job, trigger, cancellationToken);
}
await Scheduler.Start(cancellationToken);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await Scheduler?.Shutdown(cancellationToken);
}
private ITrigger CreateTrigger(JobSchedule schedule)
{
return TriggerBuilder
.Create()
.WithIdentity($"{schedule.JobType.FullName}.trigger")
.WithCronSchedule(schedule.CronExpression)
.WithDescription(schedule.CronExpression)
.Build();
}
private IJobDetail CreateJob(JobSchedule schedule)
{
var jobType = schedule.JobType;
return JobBuilder
.Create(jobType)
.WithIdentity(jobType.FullName)
.WithDescription(jobType.Name)
.Build();
}
}
解釋一下這段代碼:
這段代碼中,QuartzHostedService
有三個依賴項:Startup.ConfigureServices()
中注入的ISchedulerFactory
和IJobFactory
,以及一個IEnumerable
。在第五節的代碼中,咱們只向DI添加了一個JobSchedule
,就是DemoJob
。咱們也能夠添加多個JobSchedule
,他們都會在這個IEnumerable
中被注入到託管服務中。
StartAsync
在應用程序啓動時被調用,它是咱們配置Quartz
的地方。咱們首先建立IScheduler
的一個實例,爲它分配一個屬性供之後使用,並將調度程序的JobFactory
設置爲注入的實例:
public async Task StartAsync(CancellationToken cancellationToken)
{
Scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
Scheduler.JobFactory = _jobFactory;
//...
}
而後,循環注入的做業調度,並在類的最後使用CreateJob
和CreateTrigger
方法爲每一個做業建立一個IJobDetail
和ITrigger
。實際應用中若是有別的須要,也能夠經過擴展JobSchedule
DTO來定製它。
最後,在調度了全部做業以後,調用Scheduler.Start()
來實際在後臺啓動Quartz.NET調度器。當應用程序關閉時,框架將調用StopAsync()
,此時能夠調用Scheduler.Shutdown()
來安全地關閉調度程序進程。
所有完成後,咱們啓動QuartzHostedService
:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddHostedService<QuartzHostedService>();
}
運行程序,能夠看到結果:
demo.DemoJob: Information: Demo !
info: demo.DemoJob[0]
Demo !
demo.DemoJob: Information: Demo !
info: demo.DemoJob[0]
Demo !
demo.DemoJob: Information: Demo !
info: demo.DemoJob[0]
Demo !
demo.DemoJob: Information: Demo !
info: demo.DemoJob[0]
Demo !
本文的代碼,在https://github.com/humornif/Demo-Code/tree/master/0029/demo
![]() |
微信公衆號:老王Plus 掃描二維碼,關注我的公衆號,能夠第一時間獲得最新的我的文章和內容推送 本文版權歸做者全部,轉載請保留此聲明和原文連接 |