作業(yè)調(diào)度框架Quartz.NET-現(xiàn)學(xué)現(xiàn)用-02-任務(wù)監(jiān)聽

前言

任務(wù)調(diào)度系統(tǒng)并不是完美的,它會出現(xiàn)任務(wù)執(zhí)行失敗的情況。如果你需要處理任務(wù)失敗后的邏輯,希望這篇筆記可以為你提供些幫助。
Quartz.NET的任務(wù)監(jiān)聽系統(tǒng)已經(jīng)被我運(yùn)用在已上線的工程中,親測無坑。

Quartz.Listener

要創(chuàng)建一個監(jiān)聽器,只需創(chuàng)建一個實(shí)現(xiàn)ITriggerListener或IJobListener接口的對象。然后在運(yùn)行時(shí)向調(diào)度程序注冊監(jiān)聽器,并且必須為其指定名稱(更確切地說,他們必須通過其Name屬性來唯一標(biāo)識自己。)

關(guān)鍵接口和類

  • IJobListener - 與作業(yè)相關(guān)的事件包括:作業(yè)即將執(zhí)行的通知,以及作業(yè)完成執(zhí)行時(shí)的通知。
  • ITriggerListener - 與觸發(fā)器相關(guān)的事件包括:觸發(fā)器觸發(fā),觸發(fā)錯誤觸發(fā)和觸發(fā)器完成(觸發(fā)器觸發(fā)的作業(yè)完成)。
  • ListenerManager - 監(jiān)聽器與調(diào)度程序的ListenerManager一起注冊,并附帶一個Matcher,用于描述監(jiān)聽器想要接收事件的作業(yè)/觸發(fā)器。

示例應(yīng)用程序

using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using System;
using System.Collections.Specialized;
using System.Threading;
using System.Threading.Tasks;

namespace QuarzLis
{
    class Program
    {
        static void Main(string[] args)
        {
            StartUpJobs.StartUp().GetAwaiter().GetResult();
            Console.ReadKey();
        }

        public static class StartUpJobs
        {
            public static async Task StartUp()
            {
                try
                {
                    //第一步:從工廠中獲取Scheduler實(shí)例
                    NameValueCollection props = new NameValueCollection();
                    StdSchedulerFactory factory = new StdSchedulerFactory(props);
                    IScheduler scheduler = await factory.GetScheduler();
                    //第二步:然后運(yùn)行它
                    await scheduler.Start();
                    //第三步:定義作業(yè)并綁定到HelloJob類,HelloJob類實(shí)現(xiàn)IJob接口
                    IJobDetail job = JobBuilder.Create<HelloJob>()
                            .WithIdentity("job1", "group1")
                            //UsingJobData 可以用來傳參數(shù)
                            .UsingJobData("appKey", "123456QWE")
                            .UsingJobData("appName", "小熊貓")
                            .UsingJobData("api", "https://www.baidu.com")
                            .Build();

                    //第四步:創(chuàng)建觸發(fā)器。設(shè)定,執(zhí)行一次作業(yè)。
                    ITrigger trigger = TriggerBuilder.Create()
                        .WithIdentity("trigger1", "group1") //指定唯一標(biāo)識,觸發(fā)器名字,和組名字
                                                            //這對于將作業(yè)和觸發(fā)器組織成“報(bào)告作業(yè)”和“維護(hù)作業(yè)”等類別非常有用。
                                                            //作業(yè)或觸發(fā)器的鍵的名稱部分在組內(nèi)必須是唯一的
                        .StartAt(DateBuilder.FutureDate(5, IntervalUnit.Second)) //可以設(shè)定在未來的 5 秒鐘后觸發(fā)    
                        .Build();

                    //第五步:作業(yè)與觸發(fā)器組合,安排任務(wù)
                    await scheduler.ScheduleJob(job, trigger);

                    //第六步:創(chuàng)建任務(wù)監(jiān)聽,用來解決任務(wù)執(zhí)行失敗的情況. HelloJob類實(shí)現(xiàn)IJobListener接口
                    IJobListener jobListener = new HelloJob();

                    // 注: 任務(wù)監(jiān)聽是通過 IJobListener.Name 來區(qū)分的.以下邏輯避免多個任務(wù)監(jiān)聽情況下造成的監(jiān)聽被覆蓋.
                    // a) 獲取當(dāng)前任務(wù)監(jiān)聽實(shí)例的名稱.
                    var listener = scheduler.ListenerManager.GetJobListener(jobListener.Name);
                    // b) 通過job.Key 獲取該任務(wù)在調(diào)度系統(tǒng)中的唯一實(shí)體
                    IMatcher<JobKey> matcher = KeyMatcher<JobKey>.KeyEquals(job.Key);
                    // c) 注意: 任務(wù)監(jiān)聽系統(tǒng)中已存在當(dāng)前任務(wù)監(jiān)聽實(shí)例,與新添加任務(wù)監(jiān)聽的邏輯的區(qū)別.
                    if (listener != null)
                    {
                        // 如果已存在該任務(wù)監(jiān)聽實(shí)例,調(diào)用此方法,為該任務(wù)監(jiān)聽實(shí)例新增監(jiān)聽對象
                        scheduler.ListenerManager.AddJobListenerMatcher(jobListener.Name, matcher);
                    }
                    else
                        // 任務(wù)監(jiān)聽系統(tǒng)中不存在該任務(wù)監(jiān)聽實(shí)例,則調(diào)用此方法新增監(jiān)聽對象
                        scheduler.ListenerManager.AddJobListener(jobListener, matcher);

                    //創(chuàng)建觸發(fā)器監(jiān)聽,觸發(fā)器監(jiān)聽與任務(wù)監(jiān)聽同名也不影響
                    ITriggerListener triggerListener = new HelloJob();
                    var triListener = scheduler.ListenerManager.GetTriggerListener(triggerListener.Name);
                    IMatcher<TriggerKey> triMatcher = KeyMatcher<TriggerKey>.KeyEquals(trigger.Key);
                    if (triListener != null)
                    {
                        scheduler.ListenerManager.AddTriggerListenerMatcher(triggerListener.Name, triMatcher);
                    }
                    else
                        scheduler.ListenerManager.AddTriggerListener(triggerListener, triMatcher);

                    //可以設(shè)置關(guān)閉該調(diào)度
                    //await Task.Delay(TimeSpan.FromSeconds(5));
                    //await scheduler.Shutdown();
                }
                catch (SchedulerException se)
                {
                    Console.WriteLine(se);
                }
            }
        }

        //實(shí)現(xiàn)IJobListener 接口,實(shí)現(xiàn) ITriggerListener 接口,這里和 IJob邏輯放在了一起
        public class HelloJob : IJob, IJobListener, ITriggerListener
        {
            private string appKey;
            private string appName;
            private string appApi;

            public string Name
            {
                get;
            }
            public HelloJob()
            {
                this.Name = this.GetType().ToString();
            }
            public HelloJob(string name)
            {
                this.Name = name;
            }
            public async Task Execute(IJobExecutionContext context)
            {
                JobKey jkey = context.JobDetail.Key;
                TriggerKey tKey = context.Trigger.Key;

                JobDataMap dataMap = context.MergedJobDataMap;
                appKey = dataMap.GetString("appKey");   //通過鍵值獲取數(shù)據(jù)
                appName = dataMap.GetString("appName");
                appApi = dataMap.GetString("api");
                await Console.Error.WriteLineAsync(
                    string.Format("[{0}]開始推送:\nJobKey:{1}\nTriggerKey:{2}\nAppKey:{3} appName: {4} , and AppAPI: {5}"
                    , DateTime.Now.ToLongTimeString(), jkey, tKey, appKey, appName, appApi));
            }
            #region IJobListener
            public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Console.Error.WriteLineAsync(string.Format("[{0}]任務(wù)監(jiān)聽,name:{1}|任務(wù)執(zhí)行失敗重新執(zhí)行。"
                    , DateTime.Now.ToLongTimeString(), Name));
                //任務(wù)執(zhí)行失敗,再次執(zhí)行任務(wù)
                await Execute(context);
            }

            public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Console.Error.WriteLineAsync(string.Format("[{0}]任務(wù)監(jiān)聽,name:{1}|準(zhǔn)備執(zhí)行任務(wù)。"
                    , DateTime.Now.ToLongTimeString(), Name));
            }

            public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Console.Error.WriteLineAsync(string.Format("[{0}]任務(wù)監(jiān)聽,name:{1}|任務(wù)執(zhí)行完成。"
                    , DateTime.Now.ToLongTimeString(), Name));
            }
            #endregion

            #region ITriggerListener
            public async Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Console.Error.WriteLineAsync(string.Format("[{0}]觸發(fā)器監(jiān)聽,name:{1}|觸發(fā)器觸發(fā)成功。"
                    , DateTime.Now.ToLongTimeString(), trigger.Key.Name));
            }

            public async Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Console.Error.WriteLineAsync(string.Format("[{0}]觸發(fā)器監(jiān)聽,name:{1}|觸發(fā)器開始觸發(fā)。"
                    , DateTime.Now.ToLongTimeString(), trigger.Key.Name));
            }

            public async Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Console.Error.WriteLineAsync(string.Format("[{0}]觸發(fā)器監(jiān)聽,name:{1}|觸發(fā)器觸發(fā)失敗。"
                    , DateTime.Now.ToLongTimeString(), trigger.Key.Name));
            }

            public async Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Console.Error.WriteLineAsync(string.Format("[{0}]觸發(fā)器監(jiān)聽,name:{1}|可以阻止該任務(wù)執(zhí)行,這里不設(shè)阻攔。"
                    , DateTime.Now.ToLongTimeString(), trigger.Key.Name));
                // False 時(shí),不阻止該任務(wù)。True 阻止執(zhí)行
                return false;
            }
            #endregion
        }
    }
}

實(shí)驗(yàn)效果

如截圖所示,這里只執(zhí)行一次。注意觀察:觸發(fā)器監(jiān)聽優(yōu)先級 > 任務(wù)監(jiān)聽優(yōu)先級

image

上篇

上篇:作業(yè)調(diào)度框架Quartz.NET-01-快速入門

Thanks

Quartz.NET

張善友的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容