這是 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 意見:
張貼留言