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

前言

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

Quartz.Listener

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

關鍵接口和類

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

示例應用程序

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實例
                    NameValueCollection props = new NameValueCollection();
                    StdSchedulerFactory factory = new StdSchedulerFactory(props);
                    IScheduler scheduler = await factory.GetScheduler();
                    //第二步:然后運行它
                    await scheduler.Start();
                    //第三步:定義作業(yè)并綁定到HelloJob類,HelloJob類實現(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ā)器。設定,執(zhí)行一次作業(yè)。
                    ITrigger trigger = TriggerBuilder.Create()
                        .WithIdentity("trigger1", "group1") //指定唯一標識,觸發(fā)器名字,和組名字
                                                            //這對于將作業(yè)和觸發(fā)器組織成“報告作業(yè)”和“維護作業(yè)”等類別非常有用。
                                                            //作業(yè)或觸發(fā)器的鍵的名稱部分在組內必須是唯一的
                        .StartAt(DateBuilder.FutureDate(5, IntervalUnit.Second)) //可以設定在未來的 5 秒鐘后觸發(fā)    
                        .Build();

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

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

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

                    //創(chuàng)建觸發(fā)器監(jiān)聽,觸發(fā)器監(jiān)聽與任務監(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);

                    //可以設置關閉該調度
                    //await Task.Delay(TimeSpan.FromSeconds(5));
                    //await scheduler.Shutdown();
                }
                catch (SchedulerException se)
                {
                    Console.WriteLine(se);
                }
            }
        }

        //實現(xiàn)IJobListener 接口,實現(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}]任務監(jiān)聽,name:{1}|任務執(zhí)行失敗重新執(zhí)行。"
                    , DateTime.Now.ToLongTimeString(), Name));
                //任務執(zhí)行失敗,再次執(zhí)行任務
                await Execute(context);
            }

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

            public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Console.Error.WriteLineAsync(string.Format("[{0}]任務監(jiān)聽,name:{1}|任務執(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}|可以阻止該任務執(zhí)行,這里不設阻攔。"
                    , DateTime.Now.ToLongTimeString(), trigger.Key.Name));
                // False 時,不阻止該任務。True 阻止執(zhí)行
                return false;
            }
            #endregion
        }
    }
}

實驗效果

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

image

上篇

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

Thanks

Quartz.NET

張善友的博客

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

友情鏈接更多精彩內容