zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    foreach在引用时的陷阱
    宝塔ngnix配置tp5
    三维空间建模方法之LOD模型算法
    Weblogic部署项目三种方式
    WebLogic使用总结
    SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
    BIM特点及格式文件说明
    BIM与GIS
    三维模型格式
    单点登录
  • 原文地址:https://www.cnblogs.com/aqgy12138/p/14011785.html
Copyright © 2011-2022 走看看