1. 程式人生 > >.Net Core 3.1瀏覽器後端服務(五) 引入定時任務Quartz.Net

.Net Core 3.1瀏覽器後端服務(五) 引入定時任務Quartz.Net

一、前言

近期專案中遇到一些需求,需要定時寫入資料庫,定時重新整理快取的問題,因此需要引入任務排程機制。

我的選擇是使用 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不能執行。

五、原始碼地址

gitee地址:https://gitee.com/sirius_machao/mweb