1. 程式人生 > 實用技巧 >Asp.Net Core配置Quartz.Net

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