• ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十七节--Quartz与ABP框架Abp.Quartz及扩展


    ABP+AdminLTE+Bootstrap Table权限管理系统一期
    Github:https://github.com/Jimmey-Jiang/ABP-ASP.NET-Boilerplate-Project-CMS

    Quartz简介

    Quartz.NET是一个开源的作业调度框架,是 OpenSymphonyQuartz API 的.NET移植,它用C#写成,可用于winformasp.net应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,支持cron-like表达式等等。非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等.

    参考

    对Quartz.NET不熟悉的可以先看下
    官方学习文档:http://www.quartz-scheduler.net/documentation/index.html
    使用实例介绍:http://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html
    官方的源代码下载:http://sourceforge.net/projects/quartznet/files/quartznet/

    特性
    它一些很好的特性:

    1. 支持集群,作业分组,作业远程管理。
    2. 自定义精细的时间触发器,使用简单,作业和触发分离。
    3. 数据库支持,可以寄宿Windows服务,WebSitewinform等。

    Quartz框架的一些基础概念解释:

    名称 描述
    Scheduler 作业调度器。
    IJob 作业接口,继承并实现Execute, 编写执行的具体作业逻辑。
    JobBuilder 根据设置,生成一个详细作业信息(JobDetail)。
    TriggerBuilder 根据规则,生产对应的Trig

    实战

    • Web.config配置

    基础概念我就懒得讲了,你也懒得听了,直接上代码吧。
    首先配置一下Web.config,其实这步可有可无,可以直接跳过,只是为了配置一些常量,获取固定的时间,但是不是必要的。

        <sectionGroup name="JobList">
          <section name="Job" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
        </sectionGroup>
    
      <JobList>
        <Job>
          <!--这里是一个任务节点-->
          <add key="Url" value="http://www.baidu.com" />
          <!--需要访问的Url-->
          <add key="Hour" value="10" />
          <!--开始时间小时-->
          <!--开始时间小时,注意:这里的小时为0-23,如果是1点的话就是1,而不是01-->
          <add key="Minute" value="30" />
          <!--开始时间分钟-->
          <!--开始时间分钟,注意:同上0-59-->
        </Job>
      </JobList>
    
    • 创建service
      创建ISystemSchedulerService以及SystemSchedulerService,代码上面都有详细的注释,我就不重复了。
      接口:
    public  interface ISystemSchedulerService: IApplicationService
        {
            void StartScheduler();
        }
    

    service: SystemSchedulerService

        public class SystemSchedulerService : ISystemSchedulerService
        {
            private IScheduler _scheduler;
            public ILogger _Logger { get; set; }
            public SystemSchedulerService()
            {
                _Logger = NullLogger.Instance;
            }
    
            public void StopScheduler()
            {
                _scheduler.Shutdown();
            }
    
            public void StartScheduler()
            {
                try
                {
    
                    //这里读取配置文件中的任务开始时间
                    int hour = int.Parse(((NameValueCollection)ConfigurationManager.GetSection("JobList/Job"))["Hour"]);
                    int minute = int.Parse(((NameValueCollection)ConfigurationManager.GetSection("JobList/Job"))["Minute"]);
    
    
                    ISchedulerFactory schedulerFactory = new StdSchedulerFactory();//内存调度
                    _scheduler = schedulerFactory.GetScheduler();
    
                    //创建一个Job来执行特定的任务
                    IJobDetail myLogJob = new JobDetailImpl("myLogJob", typeof(MyLogJob));
                    //创建并定义触发器的规则(每天执行一次时间为:时:分)
                   ITrigger trigger =
                       TriggerBuilder.Create()
                           .WithDailyTimeIntervalSchedule(
                              a => a.WithIntervalInHours(24).OnEveryDay().StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(hour, minute))).Build();
      
                    _scheduler.Clear();
                    //将创建好的任务和触发规则加入到Quartz中
                    _scheduler.ScheduleJob(myLogJob, trigger);
                    //开始
                    _scheduler.Start();
    
                }
                catch (Exception ex)
                {
                    _Logger.Info(ex.Message);
                }
            }
    
        }
    

    Quartz API

    Quartz API的关键接口和类是:

    • IScheduler - 与调度程序交互的主要API。
      -IJob - 您希望由调度程序执行的组件实现的接口。
    • IJobDetail - 用于定义作业的实例。
    • ITrigger - 定义执行给定Job的时间表的组件。
    • JobBuilder - 用于定义/构建obDetail实例,它定义了Jobs的实例。
    • TriggerBuilder - 用于定义/构建触发器实例。

    一个调度程序的生命周期是由它为界的创作,通过SchedulerFactory和其有关方法的调用。一旦创建了IScheduler接口,就可以使用添加,删除和列出作业和触发器,并执行其他与调度相关的操作(例如暂停触发器)。但是,调度程序不会实际上对任何触发器(执行作业)执行操作,直到使用Start()方法启动它。

    构建作业定义的代码块使用使用流畅接口的JobBuilder创建产品IJobDetail。同样,构建触发器的代码块使用TriggerBuilder流畅的接口和特定于触发器类型的扩展方法。可能的时间延长方法是:

    • WithCalendarIntervalSchedule
    • WithCronSchedule
    • WithDailyTimeIntervalSchedule
    • WithSimpleSchedule
      image.png

    示例中的ITrigger 其实可以不读取配置信息的。这就是我说的不用配置Web.config也可以。效果如下,每隔10秒执行一次。

      ITrigger trigger =
                    TriggerBuilder.Create()
                        .WithDailyTimeIntervalSchedule(
                            a => a.WithIntervalInSeconds(10)).Build();
    

    工作和触发器

    代码:

      public class MyLogJob : JobBase, ITransientDependency
        {
            public ILogger _Logger { get; set; }
            public MyLogJob()
            {
                _Logger = NullLogger.Instance;
    
            }
    
            public override void Execute(IJobExecutionContext context)
            {
                try
                {
                    _Logger.Info("QuartzJob 任务开始运行");
    
                    for (int i = 0; i < 10; i++)
                    {
                        _Logger.InfoFormat("QuartzJob 正在运行{0}", i);
                    }
    
                    _Logger.Info("QuartzJob任务运行结束");
                }
                catch (Exception ex)
                {
                    _Logger.Error("运行异常:"+ex.Message, ex);
                }
    
            }
        }
    

    当作业的触发器触发,Execute(..)方法由调度程序的工作线程之一调用。传递给此方法的JobExecutionContext对象为作业实例提供有关其“运行时”环境的信息 ( 调度程序执行的逻辑),触发执行的触发器的逻辑。
    JobDetail对象是在Job添加到调度器时由Quartz.NET客户端创建的。它包含Job的各种属性设置,以及一个JobDataMap,它可以用来存储作业类的给定实例的状态信息
    触发器对象用于触发作业的执行(或“触发”)。当你想安排一个工作,你实例化一个触发器并“调整”它的属性来提供你想要的调度。触发器也可能有一个与它们相关的JobDataMap - 这对于传递参数到一个特定于触发器的触发的Job是很有用的。Quartz提供了一些不同的触发器类型,但最常用的类型是SimpleTrigger(接口ISimpleTrigger)和CronTrigger(接口ICronTrigger)。
    如果您需要“一次性”执行(在某个特定时间只执行一项作业),或者您需要在给定时间开始工作,并且重复执行N次,SimpleTrigger会很方便的T之间执行。如果您希望基于类似日历的时间表(例如“每个星期五,中午”或“每个月的第10天的10:15”)触发,则CronTrigger非常有用。

        public class UserInfoController : ABPCMSControllerBase
        {
    
            private readonly ISystemSchedulerService _iSystemSchedulerService;
            public UserInfoController(ISystemSchedulerService iSystemSchedulerService)
            {
    
                _iSystemSchedulerService = iSystemSchedulerService;
            }
    
            [HttpGet]
            [DontWrapResult]
            public async Task<ActionResult> GetUserInfo()
            {
                _iSystemSchedulerService.StartScheduler();
            }
    }
    

    然后我们在前台控制器里面调用ISystemSchedulerService StartScheduler()方法。然后运行项目。
    然后我们打开日志WebApp_DataLogsLogs.txt,看下效果:
    image.png
    每隔十秒钟执行一次。

    Abp.Quartz

    ABP有內建的持久化后台job队列和后台worker系统。如果对于后台workers你有更高级的计划安排需求,Quartz会是一个更好的选择。对于持久化后台job队列,Hangfire也是一个好的选择。
    ABP中Quartz应用其实蛮简单的。

    1. 首先Abp.Quartz nuget包
    2. 然后加上引入到项目中。
       [DependsOn(
         typeof(AbpQuartzModule)
            )]
      public class ABPCMSWebModule : AbpModule
        { 
         }
    
    1. 创建Jobs
       这里我们就用上面例子中的job,当然创建一个新job,你可以实现QuartzIJob接口,或者继承JobBase类(定义在Abp.Quartz包),这个类包含一些帮助属性和方法(例如日志和本地化)。
    public class MyLogJob : JobBase, ITransientDependency
    {
        public override void Execute(IJobExecutionContext context)
        {
            Logger.Info("Executed MyLogJob :)");
        }
    }
    

    4.创建调度作业
    IQuartzScheduleJobManager 接口被用来创建调度作业。你可以在类中注入该接口(或者你可以在你的模块的PostInitialize方法中解析和使用它)来调度作业。这里我们把之前创建的MyLogJob引用进来。
    效果如下:

      public async Task<ActionResult> ScheduleJob()
            {
                await _jobManager.ScheduleAsync<MyLogJob>(
                    job =>
                    {
                        job.WithIdentity("MyLogJobIdentity", "MyGroup")
                            .WithDescription("A job to simply write logs.");
                    },
                    trigger =>
                    {
                        trigger.StartNow()
                            .WithSimpleSchedule(schedule =>
                            {
                                schedule.RepeatForever()
                                    .WithIntervalInSeconds(5)
                                    .Build();
                            });
                    });
                _jobManager.Start();
                return Content("OK, scheduled!");
            }
    

    效果一样。
    image.png
    经过上面四步,就完成Abp.Quartz运用,是挺简单的。

    扩展

    SimpleTrigger

    说一下SimpleTriggerWithCronSchedule()这里有三种方式,一种就是上面说到的Web.config配置,一种是WithCronSchedule设置时间参数。还有一种是WithCronSchedule("") 拥有强大的Cron时间表达式。

    SimpleTrigger应满足你的日程安排需求,如果你需要在某个特定时刻及时执行一次作业,或者在特定时间执行一次,然后在特定时间间隔重复执行。如果你想让触发器在2018年1月13日上午11点23分54秒执行,然后再执行5次,每10秒执行一次。

    通过这个描述,你可能不会觉得奇怪的是,SimpleTrigger的属性包括:开始时间和结束时间,重复计数和重复间隔。所有这些属性都与您所期望的完全相同,只有一些与结束时间属性相关的特殊注释。

    SimpleTrigger实例是使用TriggerBuilder(用于触发器的主属性)和WithSimpleSchedule扩展方法(用于SimpleTrigger特有的属性)构建的。

    在特定的时刻建立一个触发器,不要重复:

    // trigger builder creates simple trigger by default, actually an ITrigger is returned
    ISimpleTrigger trigger = (ISimpleTrigger) TriggerBuilder.Create()
        .WithIdentity("trigger1", "group1")
        .StartAt(myStartTime) // some Date 
        .ForJob("job1", "group1") // identify job with name, group strings
        .Build();
    

    建立一个特定时刻的触发器,然后每十秒钟重复十次:

    trigger = TriggerBuilder.Create()
        .WithIdentity("trigger3", "group1")
        .StartAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied
        .WithSimpleSchedule(x => x
            .WithIntervalInSeconds(10)
            .WithRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
        .ForJob(myJob) // identify job with handle to its JobDetail itself                   
        .Build();
    

    建立一个触发器,将在未来五分钟内触发一次:

    trigger = (ISimpleTrigger) TriggerBuilder.Create()
        .WithIdentity("trigger5", "group1")
        .StartAt(DateBuilder.FutureDate(5, IntervalUnit.Minute)) // use DateBuilder to create a date in the future
        .ForJob(myJobKey) // identify job with its JobKey
        .Build();
    

    建立一个触发器,现在会触发,然后每隔五分钟重复一次,直到22:00:

    trigger = TriggerBuilder.Create()
        .WithIdentity("trigger7", "group1")
        .WithSimpleSchedule(x => x
            .WithIntervalInMinutes(5)
            .RepeatForever())
        .EndAt(DateBuilder.DateOf(22, 0, 0))
        .Build();
    

    建立一个触发器,将在下一个小时的顶部执行,然后每2小时重复一次:

    trigger = TriggerBuilder.Create()
        .WithIdentity("trigger8") // because group is not specified, "trigger8" will be in the default group
        .StartAt(DateBuilder.EvenHourDate(null)) // get the next even-hour (minutes and seconds zero ("00:00"))
        .WithSimpleSchedule(x => x
            .WithIntervalInHours(2)
            .RepeatForever())
        // note that in this example, 'forJob(..)' is not called 
        //  - which is valid if the trigger is passed to the scheduler along with the job  
        .Build();
    
    await scheduler.scheduleJob(trigger, job);
    

    另外一些常用的停止作业的指令常量

    • MisfireInstruction.IgnoreMisfirePolicy
    • MisfirePolicy.SimpleTrigger.FireNow
    • MisfirePolicy.SimpleTrigger.RescheduleNowWithExistingRepeatCount
    • MisfirePolicy.SimpleTrigger.RescheduleNowWithRemainingRepeatCount
    • MisfirePolicy.SimpleTrigger.RescheduleNextWithRemainingCount
    • MisfirePolicy.SimpleTrigger.RescheduleNextWithExistingCount

    在构建SimpleTriggers时,可以将简单的时间表(通过SimpleSchedulerBuilder)指定为停止作业指令:

    trigger = TriggerBuilder.Create()
        .WithIdentity("trigger7", "group1")
        .WithSimpleSchedule(x => x
            .WithIntervalInMinutes(5)
            .RepeatForever()
            .WithMisfireHandlingInstructionNextWithExistingCount())
        .Build();
    

    Cron时间表达式。

    Cron-Expressions用于配置CronTrigger的实例。Cron-Expressions是由七个子表达式组成的字符串,它们描述了计划的各个细节。这些子表达式用空格分隔,表示:

    • Seconds
    • Minutes
    • Hours
    • Day-of-Month
    • Month
    • Day-of-Week
    • Year (optional field)

    示例Cron表达式
    下面是一些表达式及其含义的例子 - 你可以在CronTrigger的API文档中找到更多的例子
    CronTrigger示例1 - 创建一个触发器的表达式,每5分钟触发一次
    "0 0/5 * * * ?"
    CronTrigger示例2 - 一个表达式,用于创建在分钟后10秒(即上午10:00:10,上午10:05:10等)每5分钟触发一次的触发器。
    "10 0/5 * * * ?"
    CronTrigger示例3 - 一个表达式,用于在每个星期三和星期五的10:30,11:30,12:30和13:30创建一个触发器。
    "0 30 10-13 ? * WED,FRI"
    CronTrigger示例4 - 一个表达式,用于创建一个触发器,在每个月的第5天和第20天的上午8点到上午10点之间每隔半小时触发一次。请注意,触发器不会在上午10点,仅在8点,8点,9点和9点30分
    "0 0/30 8-9 5,20 * ?"
    请注意,一些调度要求过于复杂,无法用一个触发器来表示 - 例如“上午9点至上午10点之间每5分钟一次,下午1点至10点之间每20分钟一次”。在这种情况下解决方案是简单地创建两个触发器,并注册他们两个运行相同的工作。

    CronTrigger实例使用TriggerBuilder(用于触发器的主属性)和WithCronSchedule扩展方法(用于CronTrigger特定的属性)构建。
    您也可以使用CronScheduleBuilder的静态方法来创建计划。
    建立一个触发器,每隔上午8点到下午5点,每隔一分钟一次:

    trigger = TriggerBuilder.Create()
        .WithIdentity("trigger3", "group1")
        .WithCronSchedule("0 0/2 8-17 * * ?")
        .ForJob("myJob", "group1")
        .Build();
    

    建立一个触发器,每天在上午10:42开始:

    // we use CronScheduleBuilder's static helper methods here
    trigger = TriggerBuilder.Create()
        .WithIdentity("trigger3", "group1")
        .WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(10, 42))
        .ForJob(myJobKey)
        .Build();
    

    要么 -

    trigger = TriggerBuilder.Create()
        .WithIdentity("trigger3", "group1")
        .WithCronSchedule("0 42 10 * * ?")
        .ForJob("myJob", "group1")
        .Build();
    

    建立一个触发器,将在星期三上午10:42,在系统默认的时区以外的其他时间触发:

    trigger = TriggerBuilder.Create()
        .WithIdentity("trigger3", "group1")
        .WithSchedule(CronScheduleBuilder
            .WeeklyOnDayAndHourAndMinute(DayOfWeek.Wednesday, 10, 42)
            .InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time")))
        .ForJob(myJobKey)
        .Build();
    

    要么 -

    trigger = TriggerBuilder.Create()
        .WithIdentity("trigger3", "group1")
        .WithCronSchedule("0 42 10 ? * WED", x => x
            .InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time")))
        .ForJob(myJobKey)
        .Build();
    

    以下指令可用于通知QuartzCronTrigger发生停止作业时应该执行的操作。(本教程的“更多关于触发器”部分介绍了停止作业情况)。这些指令被定义为常量(并且API文档具有对其行为的描述)。说明包括:

    • MisfireInstruction.IgnoreMisfirePolicy
    • MisfireInstruction.CronTrigger.DoNothing
    • MisfireInstruction.CronTrigger.FireOnceNow

    所有触发器都有MisfireInstrution.SmartPolicy指令可供使用,并且此指令也是所有触发器类型的默认值。CronTrigger将“智能策略”指令解释为MisfireInstruction.CronTrigger.FireOnceNow。CronTrigger.UpdateAfterMisfire()方法的API文档解释了此行为的确切详细信息。
    在构建CronTriggers时,您可以将缺火指令指定为cron时间表的一部分(通过WithCronSchedule扩展方法):

    trigger = TriggerBuilder.Create()
        .WithIdentity("trigger3", "group1")
        .WithCronSchedule("0 0/2 8-17 * * ?", x => x
            .WithMisfireHandlingInstructionFireAndProceed())
        .ForJob("myJob", "group1")
        .Build();
    

    其他

    我们可以看一下,源码中对IQuartzScheduleJobManagerScheduleAsync方法的封装,其实就是Scheduler.ScheduleJob处理了一下。
    image.png

    如果要使用模块的PostInitialize方法中解析和使用它来调度作业,也是可以的。

    using System.Reflection;
    using Abp.Dependency;
    using Abp.Modules;
    using Abp.Quartz.Configuration;
    using Abp.Threading.BackgroundWorkers;
    using Quartz;
    
    namespace Abp.Quartz
    {
        [DependsOn(typeof (AbpKernelModule))]
        public class AbpQuartzModule : AbpModule
        {
            public override void PreInitialize()
            {
                IocManager.Register<IAbpQuartzConfiguration, AbpQuartzConfiguration>();
    
                Configuration.Modules.AbpQuartz().Scheduler.JobFactory = new AbpQuartzJobFactory(IocManager);
            }
    
            public override void Initialize()
            {
                IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
            }
    
            public override void PostInitialize()
            {
                IocManager.RegisterIfNot<IJobListener, AbpQuartzJobListener>();
    
                Configuration.Modules.AbpQuartz().Scheduler.ListenerManager.AddJobListener(IocManager.Resolve<IJobListener>());
    
                if (Configuration.BackgroundJobs.IsJobExecutionEnabled)
                {
                    IocManager.Resolve<IBackgroundWorkerManager>().Add(IocManager.Resolve<IQuartzScheduleJobManager>());
                }
            }
        }
    }
    
    

    JobBase封装的一些方法,继承自IJob
    image.png

    ABP+AdminLTE+Bootstrap Table权限管理系统一期
    Github:https://github.com/Jimmey-Jiang/ABP-ASP.NET-Boilerplate-Project-CMS

    您的资助是我最大的动力!
    金额随意,欢迎来赏!

    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
    如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我

    如果,想给予我更多的鼓励,求打

    因为,我的写作热情也离不开您的肯定支持,感谢您的阅读,我是【安与生】!

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    fork 开源项目后如何参与项目
    C51 头文件中的 extern
    windows常用命令
    boost(barrier)
    boost库(条件变量)
    线程间同步的几种方式(了解)
    stl 迭代器(了解)
    std::thread
    同步,异步,阻塞,非阻塞
    std::thread join和detach区别
  • 原文地址:https://www.cnblogs.com/anyushengcms/p/8037569.html
Copyright © 2020-2023  润新知