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

来源:互联网 发布:淘宝客新手如何玩鹊桥 编辑:程序博客网 时间:2024/06/04 18:43

  • Quartz简介
    • 参考
    • 实战
    • Quartz API
    • 工作和触发器
  • AbpQuartz
    • 扩展
    • Cron时间表达式
    • 其他

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();        }}

然后我们在前台控制器里面调用ISystemSchedulerServiceStartScheduler()方法。然后运行项目。
然后我们打开日志Web\App_Data\Logs\Logs.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 returnedISimpleTrigger 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 heretrigger = 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

阅读全文
0 0
原创粉丝点击