.NET Core的Quartz实战之自定义配置实现

本文介绍了如何在.NET应用中通过NuGet引入Quartz库进行任务调度,包括创建BackgroundService、实现IDisposable接口确保资源管理,以及自定义注解和配置模型。重点强调了IJobFactory的实现以解决服务注入问题。
该文章已生成可运行项目,

1、通过NuGet引入Quartz

2、QuartzBackgroundService 需继承BackgroundService(当项目启动的时候,会进行调用,java也有许多这种内置的类,像beanFactory这些)

但是QuartzBackgroundService这个必须注册为本地服务类,如下

services.AddHostedService<QuartzBackgroundService>();//非常重要

详细代码:

    public class QuartzBackgroundService : BackgroundService
    {
        private readonly IServiceProvider _serviceProvider;

        public QuartzBackgroundService(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var scheduleService = _serviceProvider.GetService<QuartzConfigService>();
            await scheduleService.init();
        }
    }

3、QuartzConfigService需实现IDisposable这个接口,该接口是c#中用来实现释放资源的,当程序关闭时,会调用Dispose()方法用来释放资源

详细代码:

    public class QuartzConfigService : IDisposable
    {
        private readonly IScheduler _scheduler;
        private readonly AppSettings _appSettings;
        private readonly IServiceProvider _serviceProvider;
        private readonly List<JobKey> jobKeys = new();

        public QuartzConfigService(ISchedulerFactory schedulerFactory, QuartzJobFactory quartzJobFactory,
            AppSettings appSettings, IServiceProvider serviceProvider)
        {
            _appSettings = appSettings;
            _serviceProvider = serviceProvider;
            _scheduler = schedulerFactory.GetScheduler().Result;
            _scheduler.JobFactory = quartzJobFactory;
        }

        private IServiceScope getScope()
        {
            return _serviceProvider.CreateScope();
        }

        private List<QuartzJobConfig> getJobConfigs()
        {
            return _appSettings.jobConfigs;
        }
        
        /// <summary>
        /// 初始化
        /// </summary>
        public async Task init()
        {
            string msg = "******init ScheduleService start******";
             
            //获取配置
            List<QuartzJobConfig> jobConfigs = getJobConfigs();
            //获取临时有效的作用域
            using var scope = getScope();
            //扫描jobs
            var jobTypes = scanJobTypes().ToList();
            //未有jobs
            if (jobTypes.Count == 0)
            {
                msg = "******init ScheduleService fail******:reason:no jobs";
                 
                return;
            }

            //启动任务调度
            await _scheduler.Start();
            //任务调度
            await schedulingTasksConfig(jobTypes, jobConfigs);

            msg = $"******init ScheduleService end******:total===>{jobKeys.Count}";
             
        }

        /// <summary>
        /// 执行任务调度的配置初始化
        /// </summary>
        /// <param name="jobTypes"></param>
        /// <param name="jobConfigs"></param>
        private async Task schedulingTasksConfig(IEnumerable<Type> jobTypes, List<QuartzJobConfig> jobConfigs)
        {
            foreach (var jobType in jobTypes)
            {
                QuartzJobAttribute quartzJobAttribute = (QuartzJobAttribute) Attribute.GetCustomAttribute(jobType, typeof(QuartzJobAttribute));
                string name = null;
                JobKey jobKey = null;
                string cron = null;
                if (null == quartzJobAttribute)
                {
                    //未使用注解 实现了IJob接口 采取默认配置方式 
                    //获取类名
                    string className = jobType.Name;
                }
                else
                {
                    string jobName = quartzJobAttribute.name;
                    //根据name匹配config
                    var scheduleJobConfig = jobConfigs.FirstOrDefault(c => c.name.Equals(jobName));
                    //scheduleJobConfig不为空且状态是未被禁用
                    if (scheduleJobConfig is {disabled: false})
                    {
                        name = scheduleJobConfig.name;
                        jobKey = JobKey.Create(name);
                        cron = scheduleJobConfig.cron;
                        await addJob(jobType, jobKey, cron);
                    }
                }

           
            }
        }

        /// <summary>
        /// 扫描IJob的实现类
        /// </summary>
        /// <returns></returns>
        private static IEnumerable<Type> scanJobTypes()
        {
            // 获取当前程序集
            Assembly assembly = Assembly.GetExecutingAssembly();
            //扫描IJob的子类
            var jobTypes = assembly.GetTypes().Where(t => typeof(IJob).IsAssignableFrom(t)
                                                          && t.IsClass
                                                          && !t.IsAbstract);

            return jobTypes;
        }

        /// <summary>
        /// addJob
        /// </summary>
        /// <param name="jobType"></param>
        /// <param name="jobKey"></param>
        /// <param name="cron"></param>
        private async Task addJob(Type jobType, JobKey jobKey, string cron)
        {
            string msg = $"add job:{jobKey.Group},{jobKey.Name}";

            //IJobDetail是对任务的详细描述
            IJobDetail jobDetail = JobBuilder.Create(jobType)
                .WithIdentity(jobKey)
                .Build();
            //触发器
            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity(jobKey.Name)
                .StartNow()
                .WithCronSchedule(cron)
                .Build();
            //jobDetail与trigger绑定
            await _scheduler.ScheduleJob(jobDetail, trigger);

            //addToList
            jobKeys.Add(jobKey);
        }

        /// <summary>
        /// 程序结束 释放资源
        /// </summary>
        public async void Dispose()
        {
            foreach (var jobKey in jobKeys)
            {
                TriggerKey triggerKey = new TriggerKey(jobKey.Name, jobKey.Group);
                await _scheduler.PauseJob(jobKey); // 停止触发器
                await _scheduler.UnscheduleJob(triggerKey); // 移除触发器
                string msg = $"delete job:{jobKey.Group},{jobKey.Name}";
                 
                await _scheduler.DeleteJob(jobKey); //删除任务
            }
        }
    }

4、上面使用了扫描注解的方式,因此我们需要一个自定义注解,QuartzJobAttribute,详细代码:

/// <summary>
    /// 在实现了IJob的子类上加上这个注解 为Jobs的任务名字
    /// </summary>
    public class QuartzJobAttribute : Attribute
    {
        public string name { get; set; }

        public QuartzJobAttribute(string name)
        {
            this.name = name;
        }
    }

5、配置model类:QuartzJobConfig

   public class QuartzJobConfig
    {
        /// <summary>
        /// 任务名
        /// </summary>
        public string name { get; set; }
        
        /// <summary>
        /// cron表达式
        /// </summary>
        public string cron { get; set; }

        /// <summary>
        /// 任务状态:false未禁用 true禁用
        /// </summary>
        public bool disabled { get; set; }
    }

6、超级重要,实现IJobFactory,防止实现了IJob类的服务类无法注入service对象,这是一个大坑,上代码:

/// <summary>
    /// 手动实现Quartz定时器工厂 避免Jobs无法注入service的问题
    /// </summary>
    public class QuartzJobFactory : IJobFactory
    {
        /// <summary>
        /// 服务提供器
        /// </summary>
        private readonly IServiceProvider _serviceProvider;

        public QuartzJobFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
        {
            IServiceScope serviceScope = _serviceProvider.CreateScope();
            IJob job;
            try
            {
                Type jobType = bundle.JobDetail.JobType;
                job = (IJob) serviceScope.ServiceProvider.GetService(jobType);
            }
            catch
            {
                serviceScope.Dispose();
                throw;
            }

            return job;
        }

        /// <summary>
        /// 清理Quartz任务
        /// </summary>
        public void ReturnJob(IJob job)
        {
            if (job == null)
            {
                return;
            }

            IDisposable disposable = job as IDisposable;
            disposable?.Dispose();
        }
    }

7、使用的方式超级简单:

(1)在实现了IJob的实现类上面加上注解[QuartzJob("任务名字")]

(2)appsettings.json加上这个配置,记得在AppSettings的这个大括号里面

        "jobConfigs": [
            {
                "name": "任务名字",//必须和在实现类上添加注解的名字一摸一样 否则不会调度该任务
                "cron": "", //cron表达式
                "disabled": false//是否禁用
            },
         ]

(3)在AppSettings.cs这个类中加入

  public List<QuartzJobConfig> jobConfigs
        {
            get; set;
        }

然后最后记得将有些类注入到Service中在stratUp类里面,注入这个东西,如果不懂就评论区问吧,累了,哈哈哈哈

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值