• .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-api

    天行健,君子以自强不息; 地势坤,君子以厚德载物;
  • 相关阅读:
    [荐]推荐一个shell学习的网站
    [转]linux远程登入不需要密码
    [转] eclipce使用vim 开启装逼模式
    Linux 下查找指令
    nmon 工具的使用
    LaTeX 符号大全
    vim 粘贴复制操作
    linux命令模式下如何切换首行和尾行
    fish 与oh-my-fish 的安装
    vim 粘贴复制操作
  • 原文地址:https://www.cnblogs.com/mchao/p/14527604.html
Copyright © 2020-2023  润新知