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}\r\n" +
                    $"Trigger:{trigger.Key}\r\n" +
                    $"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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM