Asp.Net Core配置Quartz.Net

介绍

Quartz.Net是一个强大、开源、轻量的作业调度框架。在平时开发中主要用于一些定时任务开发,譬如定时发送右键,定时同步第三方数据等等。

github:https://github.com/quartznet/quartznet

官网:https://www.quartz-scheduler.net/documentation/quartz-3.x/quick-start.html

目标

这次博客主要实现任务的启动,暂停,恢复

封装

为了项目的低耦合,在封装Quartz的时候将它单独封装在一个类库中,便于管理。

  先创建一个类库,定义好类库名字。

安装nuget包。

Job工厂

在quartz中,在实现Job的时候会实例化一个JobFactory,翻译过来就是Job工厂,通过查看源码我找到了一个SimpleJobFactory,这是它的默认实现,它做的事情主要是实例化实现IJob的类。

那么自定义一个自己适用的Job工厂。

在这里的想法是,在IOC中放入Job,然后再工厂中获取容器中的Job对象。事实上,你实现工厂的核心就是定义IJob实现类的实例化规则!

因为我需要对.net core ioc进行操作,所以安装一个Microsoft.Extensions.DependencyInjection nuget包

代码:

/// <summary>
    /// 自定义job工厂
    ///  https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/miscellaneous-features.html#plug-ins
    /// </summary>
    public class ZeroJobFactory : IJobFactory
    {
        readonly IServiceProvider _provider;
        public ZeroJobFactory(IServiceProvider provider)
        {
            _provider = provider;
        }
        public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
        {
            try
            {
                //从Quartz.net的源码实现net core注入这一块能够发现,job实例是通过AddTransient加入容器中的
                //还有自定义的JobFactory也需要单例注入,我觉的是因为如果不单例注入会导致Quartz使用默认的SimpleJobFactory
                //从而导致这里的获取Job实例出问题。
                var service = _provider.CreateScope();
                var job = service.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob;
                return job;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 允许工厂释放Job
        /// </summary>
        /// <param name="job"></param>
        public void ReturnJob(IJob job)
        {
            var disposable = job as IDisposable;
            disposable?.Dispose();
        }

创建Job

这个类必须要继承IJob,否则是没用的。

    [DisallowConcurrentExecution]
    [PersistJobDataAfterExecution]
    public class FirstJob : IJob
    {
        public async Task Execute(IJobExecutionContext context)
        {
            await Task.Run(() =>
            {
                Console.WriteLine("First Job");
                //job详情
                var jobDetails = context.JobDetail;
                //触发器的信息
                var trigger = context.Trigger;
                Console.WriteLine($"JobKey:{jobDetails.Key},Group:{jobDetails.Key.Group}
" +
                    $"Trigger:{trigger.Key}
" +
                    $"RunTime:{context.JobRunTime}" +
                    $"ExecuteTime:{DateTime.Now}");
            });
        }
    }

控制中心

在自定义Job工厂和Job之后,那如何统一控制呢,这里说到控制中心,在控制中心里实现Job的运行,暂停,恢复等功能。

首先定义一个抽象的任务调度的控制中心,在.net core大多数时候都是面向抽象编程,所以这个控制中心定义为抽象的,便于依赖注入从而能够方便的在API中进行任务调度。

/// <summary>
    /// quartz 抽象调度中心
    /// </summary>
    public interface IControllerCenter
    {
        /// <summary>
        /// 启动任务调度
        /// </summary>
        /// <returns></returns>
        Task Start();
        /// <summary>
        /// 运行Job
        /// </summary>
        /// <returns></returns>
        Task RunJob();
        /// <summary>
        /// 暂停Job
        /// </summary>
        /// <returns></returns>
        Task PauseJob();
        /// <summary>
        /// 回复job
        /// </summary>
        /// <returns></returns>
        Task ResumeJob();
    }

实现任务中心

/// <summary>
    /// Quartz任务调度控制中心
    /// </summary>
    public class ControllerCenter : IControllerCenter
    {

        /// <summary>
        /// 构造函数注入自定义Job工厂
        /// </summary>
        readonly IJobFactory _jobFactory;
        private Task<IScheduler> _scheduler;

        public Task<IScheduler> Center => _scheduler ?? throw new ArgumentNullException("Schedule can not null");
        private IScheduler Scheduler => _scheduler.Result;
        public ControllerCenter(IJobFactory factory)
        {
            _jobFactory = factory;
            _scheduler = GetScheduler();
            _scheduler.Result.JobFactory = _jobFactory;
        }

        private Task<IScheduler> GetScheduler()
        {
            if (_scheduler != null)
                return _scheduler;
            else
            {
                /*
                 * 配置二进制策略
                 *https://www.quartz-scheduler.net/documentation/quartz-3.x/migration-guide.html#packaging-changes
                 */
                var properties = new NameValueCollection
                {
                    ["quartz.serializer.type"] = "binary"
                };
                //实例化工厂
                ISchedulerFactory sf = new StdSchedulerFactory(properties);
                this._scheduler = sf.GetScheduler();
                return _scheduler;
            }
        }
        public async Task Start()
        {
            try
            {
                if (this.Scheduler.IsStarted)
                {
                    Console.WriteLine("quartz is  started");
                }
                else
                {
                    Console.WriteLine("quartz start!");
                    await Scheduler.Start();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        public async Task RunJob()
        {
            var jobKey = new JobKey("job1", "group1");
            if (await Scheduler.CheckExists(jobKey))
            {
                Console.WriteLine("JobKey  Exists");
            }
            else
            {
                Console.WriteLine("JobKey Allow");
                if (!this.Scheduler.IsStarted)
                {
                    Console.WriteLine("quartz is not  started");
                    await this.Start();
                }
                var job = JobBuilder.Create<FirstJob>()
                   .WithIdentity("job1", "group1")
                   .Build();
                var trigger = TriggerBuilder.Create()
                   .WithIdentity("trigger1", "group1")
                    .StartNow()
                    .WithSimpleSchedule(a =>
                    {
                        a.RepeatForever();//永远执行
                        a.WithIntervalInSeconds(3);//执行间隔3s
                    })
                    .ForJob(job)
                    .Build();

                await Scheduler.ScheduleJob(job, trigger);
            }
        }
        public async Task PauseJob()
        {
            var jobKey = new JobKey("job1", "group1");
            if (await Scheduler.CheckExists(jobKey))
            {
                await Scheduler.PauseJob(jobKey);
                Console.WriteLine("PauseJob Success!");
            }
            else
            {
                Console.WriteLine("Not IsExists JobKey");
            }
        }

        public async Task ResumeJob()
        {
            var jobKey = new JobKey("job1", "group1");
            if (await Scheduler.CheckExists(jobKey))
            {
                await Scheduler.ResumeJob(jobKey);
                Console.WriteLine("ResumeJob Success!");
            }
            else
            {
                Console.WriteLine("Not IsExists JobKey");
            }
        }

可以看到我在RunJob中已经将创建的Job放入,接下来就要说到如何通过控制中心去使用了。

使用

在.net core Startup中将自定义工厂,控制中心放入容器。

  //自定义Job工厂
            services.AddSingleton<IJobFactory, ZeroJobFactory>();
            //任务调度控制中心
            services.AddSingleton<IControllerCenter, ControllerCenter>();
            //Jobs,将组件好的Job放在这里,生命周期为瞬时的
            services.AddTransient<FirstJob>();

创建一个控制器,直接上代码

[Route("api/[controller]")]
    [ApiController]
    public class QuartzController : ControllerBase
    {
        readonly IControllerCenter _center;
        public QuartzController(IControllerCenter center)
        {
            _center = center;
        }
        /// <summary>
        /// 开启任务调度
        /// </summary>
        /// <returns></returns>
        [HttpGet("Start")]
        public async Task<JsonResult> Start()
        {
            await _center.Start();
            return AjaxHelper.Seed(Ajax.Ok);
        }
        /// <summary>
        /// 运行Job
        /// </summary>
        /// <returns></returns>
        [HttpGet("Run")]
        public async Task<JsonResult> Run()
        {
            await _center.RunJob();
            return AjaxHelper.Seed(Ajax.Ok);
        }
        /// <summary>
        /// 暂停Job
        /// </summary>
        /// <returns></returns>
        [HttpGet("PauseJob")]
        public async Task<JsonResult> PauseJob()
        {
            await _center.PauseJob();
            return AjaxHelper.Seed(Ajax.Ok);
        }
        /// <summary>
        /// 恢复Job
        /// </summary>
        /// <returns></returns>
        [HttpGet("ResumeJob")]
        public async Task<JsonResult> ResumeJob()
        {
            await _center.ResumeJob();
            return AjaxHelper.Seed(Ajax.Ok);
        }
    }

代码写好,就来测试一下。

测试

首先测试启动任务调度和运行Job

 注意看控制台的打印的信息,跟我们在Job中定义的内容是一样的,而且我在触发器中指定了没3s执行一次,可以从执行时间看到是实现了的。

既然任务启动了,那我就试着将它暂停下吧。

 可以从打印信息看出,我在暂停之后又将它恢复使用。这里需要注意的一点是,如果Job没有启动是没法根据JobKey去暂停的。

总结

今天对Quartz结合.net core进行了简单的配合使用。完成了博客开头说到的目标:启动,暂停,恢复

总的来说Quartz是一个非常好玩的东西的,但是我在动手之前是花了近乎一周的时间去看文档,看代码,看优秀.neter对它的使用方式。

在了解到足够多的信息才去动手做的。这一点的做法对我的帮助是非常大,在做的过程中遇到的阻碍相较来说少了很多。

源码地址:https://github.com/QQ2287991080/Zero.Core

原文地址:https://www.cnblogs.com/aqgy12138/p/14011785.html