這是 ASP.NET Core 建立排程服務 - 使用 Generic Host 搭配 Quartz.Net 系列文第二篇,這系列文的目標是使用 ASP.NET Core Generic Host 搭配排程套件 Quartz.Net,程式註冊為 Windows Service 服務執行,
在上一篇介紹了在 main 方法中建立 HostBuilder 並加入 TimeHostedService 執行,這一篇是重點是在 ASP.NET Core 中使用 Quartz.Net 排程套件,若有問題或是錯誤的地方歡迎各位高手給予指導。
GenericHostLab SampleCode 傳送門 : http://bit.ly/2Y1mqYNQuartz
上一步建立完 Generic Host 基本設定之後,接下來另一個重點就是使用 Quartz 來建立排程機制,Quartz.Net 是一套功能齊全的工作排程框架,由 Java 熱門的排程框架 Quartz 移植到 .NET 上,open source 且提供彈性的設定讓開發者使用,安裝方式與一般套件相同,首先先到 Nuget Package Manage 搜尋 "quartz",安裝目前最新版的 Quartz.NET 套件
或是在 Nuget Package Console 輸入指令
Install-Package Quartz完畢後可以到 .csproj 專案中查看是否有安裝成功
<ItemGroup>
<PackageReference Include="Quartz" Version="3.0.7" />
</ItemGroup>
建立 IJob & IJobFactory
完成 quartz.net 的安裝動作之後,接下來是設定 Quartz 相關設定與配置,之前在 Quartz.NET 初體驗 中
有介紹過在 Quartz.NET 中有幾個重要的元件像是 Job 跟 Schedule,需要客製的話需要實作各自的 interface,第一步先來建立 TestJob 類別,並實作 Execute 方法將我們要處理的內容邏輯寫在裡面,由於是範例代碼就印出現在的時間,另外可以在 TestJob 加上 DisallowConcurrentExecution 預防相同 Job 同時間重複執行
namespace GenericHostLab.Job
{
[DisallowConcurrentExecution]
public class TestJob : IJob
{
private readonly ILogger _logger;
public TestJob(ILogger<TestJob> logger)
{
this._logger = logger;
}
public Task Execute(IJobExecutionContext context)
{
_logger.LogInformation($"{DateTime.Now} : TestJob Execute ...");
return Task.CompletedTask;
}
}
}
下一步是建立 SingletonJobFactory 類別,主要作用是實作 IJobFactory 接口並透過工廠方法來確保新增 IJob 是安全的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) { }
}
建立 Schedule
一般來說會是多個排程同時執行,為了建立 Job 方便建立一個 JobSchedule 類別其中定義了 JobType 與設定 CronJob 排程的執行時間,Cron expressions 表達式可以定義 Job 所需要執行頻率的規則,例如我排程執行的時間/頻率是每 3 秒鐘會執行一次,在 Cron 就使用 0/3 * * * * ? 來表達,這裡推薦 Cron expression 產生器 讓我們可以更方便的產生需要的 CronTrigger
public class JobSchedule
{
public JobSchedule(Type jobType, string cronExpression)
{
JobType = jobType;
CronExpression = cronExpression;
}
public Type JobType { get; }
public string CronExpression { get; }
}
接著就是要將 JobSchedule、SingletonJobFactory 在啟動時註冊至 Service中,在 .NET Core DI 內建提供三種服務容器生命週期 (LifeTime) 可提供開發者設定,這裡都使用 Singleton 方式讓使用時在 Application 只會有一份 Instance
實作 IHostedService.ConfigureServices((hostContext, services) =>
{
services.Configure<HostOptions>(option =>
{
option.ShutdownTimeout = TimeSpan.FromSeconds(30);
});
services.AddSingleton<IJobFactory, SingletonJobFactory>();
services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
var testJob = new JobSchedule(jobType: typeof(TestJob),cronExpression: "0/3 * * * * ?");
services.AddSingleton<TestJob>();
services.AddSingleton(testJob);
接著建立 QuartzHostedService 類別並實作 IHostedService 介面,上一篇有對 IHostedService 做簡單介紹,我們可以透過其特性在 QuartzHostedService 類別中定義我們所想要在應用程式或服務啟動與關閉時的工作,代碼如下
namespace GenericHostLab.Service
{
public class QuartzHostedService: IHostedService
{
private readonly ISchedulerFactory _schedulerFactory;
private readonly IJobFactory _jobFactory;
private readonly IEnumerable<JobSchedule> _jobSchedules;
private readonly ILogger<QuartzHostedService> _logger;
private IScheduler _scheduler;
public QuartzHostedService(ILoggerFactory loggerFactory,
ISchedulerFactory schedulerFactory, IEnumerable<JobSchedule> jobSchedules, IJobFactory jobFactory)
{
_logger = loggerFactory.CreateLogger<QuartzHostedService>();
_schedulerFactory = schedulerFactory;
_jobSchedules = jobSchedules;
_jobFactory = jobFactory;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("QuartzHostedService Start...");
_scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
_scheduler.JobFactory = _jobFactory;
foreach (var schedule in _jobSchedules)
{
await _scheduler.ScheduleJob(
JobBuilder
.Create(schedule.JobType)
.WithIdentity(schedule.JobType.FullName)
.WithDescription(schedule.JobType.Name)
.Build(),
TriggerBuilder
.Create()
.WithIdentity($"{schedule.JobType.FullName}.trigger")
.WithCronSchedule(schedule.CronExpression)
.WithDescription(schedule.CronExpression)
.Build()
, cancellationToken);
}
await _scheduler.Start(cancellationToken);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("QuartzHostedService Stop...");
await _scheduler?.Shutdown(cancellationToken);
}
}
}
代碼內容首先在建構子使用 DI 注入 schedulerFactory、jobSchedules 與 jobFactory,QuartzService 重點則是 StartAsync 方法 Quartz 設定也就是在 StartAsync 方法中進行,使用 foreach 將所有要執行的 jobSchedule 逐一加到 _scheduler 中,再透過 _scheduler.Start 來啟動排程;於 StopAsync 方法中加入將 schedule shotdown 停止排程運作的邏輯;另外為了觀察是否正常運作,我在 Start 與 Stop 方法都有加上 log 觀察輸出結果。最後一步是將 QuartzHostedService 加入 service 中,在 ConfigureServices 使用 AddHostedService 擴充方法將 QuartzHostedService 加入 service,代碼如下
.ConfigureServices((hostContext, services) =>
{
// 省略
services.AddHostedService<QuartzHostedService>();
}
接著執行應用程式,可以看到應用程式啟動後每三秒在 console 畫面顯示目前的時間,也可以從 console log 看到運行 QuartzJostedServer Start 的紀錄
以上就完成 Quartz.NET 在 Gereric Host 的應用,大功告成打完收工(?
但之前提到過最後的目標是放在 Windows Service 中執行,因此下一篇將繼續介紹如何將 Generic Host 註冊 Windows Server 的流程與設定,如果這篇有不清楚或是看不懂的地方歡迎提出來一起討論 :)
但之前提到過最後的目標是放在 Windows Service 中執行,因此下一篇將繼續介紹如何將 Generic Host 註冊 Windows Server 的流程與設定,如果這篇有不清楚或是看不懂的地方歡迎提出來一起討論 :)
參考
Implement background tasks in microservices with IHostedService and the BackgroundService class
Creating a Quartz.NET hosted service with ASP.NET Core
.NET 泛型主機


0 意見:
張貼留言