一、前言
近期項目中遇到一些需求,需要定時寫入數據庫,定時刷新緩存的問題,因此需要引入任務調度機制。
我的選擇是使用 Quartz.Net,使用的版本是 3.2.4
這里強調一點:3.x的版本與2.x的版本使用方式有一定的差別,需要注意一下!!!
什么是Quartz.NET? Quartz.NET官方文檔
Quartz.NET 是一個功能齊全的開源作業調度系統,可用於從最小的應用程序到大型企業系統。
二、Quartz.Net機制圖
三、.Net Core中引入Quartz
新建.Net Core 類庫項目命名為 MCronJob
創建完成后引入Quartz.Net
在Package Manager Console輸入如下命令 安裝Quartz包
Install-Package Quartz
由Quartz.Net關系圖可知,我們需要JobFactory和實際任務類HelloJob
創建 CronJobFactory 類並實現 IJobFactory接口 ------------------任務工廠
public class CronJobFactory : IJobFactory { public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { throw new NotImplementedException(); } public void ReturnJob(IJob job) { throw new NotImplementedException(); } }
這里打個問號?NewJob和ReturnJob兩個方法用來做什么?---------我是伏筆①
創建HelloJob類並實現 IJob接口 ---------------------------自定義的實際任務
public class HelloJob : IJob { public async Task Execute(IJobExecutionContext context) { await Console.Out.WriteLineAsync($"{DateTime.Now:HH:mm:ss}--Hello World!"); } }
有了任務工廠和任務,我們接下來實現具體的任務調度
新建SchedulerCenter類 ---------------------------------------------調度中心
public class SchedulerCenter { private readonly IJobFactory _jobFactory; private readonly ISchedulerFactory _schedulerFactory; private IScheduler _scheduler; public SchedulerCenter(IJobFactory jobFactory, ISchedulerFactory schedulerFactory) { _jobFactory = jobFactory; _schedulerFactory = schedulerFactory; } public async void StartScheduler() { //1、從工廠獲取調度程序實例 _scheduler = await _schedulerFactory.GetScheduler(); // 替換默認工廠 //_scheduler.JobFactory = this._jobFactory; //2、打開調度器 await _scheduler.Start(); //3、定義作業詳細信息並將其與HelloJob任務相關聯 IJobDetail job = JobBuilder.Create<HelloJob>() .WithIdentity("HelloJob", "HelloJobGroup") .Build(); //4、配置觸發條件:立即觸發作業運行,然后每10秒重復一次 ITrigger trigger = TriggerBuilder.Create() .WithIdentity("HelloJob", "HelloJobGroup") .StartNow() .WithSimpleSchedule(x => x .WithIntervalInSeconds(10) .RepeatForever()) .Build(); //5、將作業與觸發條件添加到調度實例並進行關聯 await _scheduler.ScheduleJob(job, trigger); } public void StopScheduler() { _scheduler?.Shutdown(true).Wait(30000); _scheduler = null; } }
SchedulerCenter 中定義了兩個方法 StartScheduler(開啟調度)和StopScheduler(停止調度)
StartScheduler方法中關於任務配置與官網相同,這里不再贅述 Quartz.Net官方配置
這里注掉了一段代碼 替換默認工廠 ---------我是伏筆②
接下來我們要啟動這個定時任務,在Startup類的ConfigureServices方法中進行注入
//注入調度中心 services.AddSingleton<SchedulerCenter>(); //注入Quartz任何工廠及調度工廠 services.AddSingleton<IJobFactory, CronJobFactory>(); services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>(); //注入HelloJob services.AddTransient<HelloJob>();
在Startup類的Configure方法中進行啟動配置
//獲取調度中心實例 var quartz = app.ApplicationServices.GetRequiredService<SchedulerCenter>(); lifetime.ApplicationStarted.Register(() => { quartz.StartScheduler(); //項目啟動后啟動調度中心 }); lifetime.ApplicationStopped.Register(() => { quartz.StopScheduler(); //項目停止后關閉調度中心 });
運行截圖:
成功啟動定時任務
其他方式啟動定時任務: Net Core 官方使用方式
四、Job實現服務注入
HelloJob類中注入用戶信息服務,輸出用戶信息,改動如下:
public class HelloJob : IJob { private readonly IUserInfoServices _userInfoServices; public HelloJob(IUserInfoServices userInfoServices) { _userInfoServices = userInfoServices; } public async Task Execute(IJobExecutionContext context) { var userInfo = _userInfoServices.GetUserInfo(); await Console.Out.WriteLineAsync($"{DateTime.Now:HH:mm:ss}--姓名:{userInfo.UserName},年齡:{userInfo.Age},地址:{userInfo.Address}"); } }
再次運行項目,等待了一段時間發現並沒有我們想要的輸出(這里折騰我好久。。。)
這時回看剛剛埋下的伏筆① ,IJobFactory接口中有兩個方法,NewJob和ReturnJob,這倆個方法做什么用?
NewJob方法: 注解的大概意思是:當觸發器觸發時獲取一個Job實例供調度器執行,那么如何獲取的Job實例呢?
我們看下NewJob第一個參數 TriggerFiredBundle,轉到TriggerFiredBundle定義,看其為我們提供了什么。
如上圖所示:TriggerFiredBundle中可以獲取到IJobDetail
IJobDetail中有JobType屬性也就是我們定義的Job類型,我們要做的就是獲取到Job實例
安裝 Microsoft.AspNetCore.Antiforgery包
Install-Package Microsoft.AspNetCore.Antiforgery -Version 2.2.0
改動Job工廠類如下所示:
public class CronJobFactory : IJobFactory { private readonly IServiceProvider _serviceProvider; public CronJobFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); } public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob; } public void ReturnJob(IJob job) { } }
接着打開SchedulerCenter中 伏筆② 的注釋:替換默認工廠
// 替換默認工廠 _scheduler.JobFactory = this._jobFactory;
再次運行,用戶服務成功調用
猜測:替換默認任務工廠后 IServiceProvider 取到的HelloJob是 ConfigureServices 中使用依賴注入的方式取到的實例,可取到UserInfoServices;
而未替換默認任務工廠取到的HelloJob並不能取到UserInfoServices,故HelloJob的Execute不能執行。