ABP module-zero +AdminLTE+Bootstrap Table+jQuery許可權管理系統第十七節--Quartz與ABP框架Abp.Quar

安與生發表於2017-12-14

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不熟悉的可以先看下 官方學習文件:www.quartz-scheduler.net/documentati… 使用例項介紹:www.quartz-scheduler.net/documentati… 官方的原始碼下載:sourceforge.net/projects/qu…

特性 它一些很好的特性:

  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 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

相關文章