C#作業調度Quartz.NET學習筆記

来源:https://www.cnblogs.com/atomy/archive/2020/05/20/12899038.html
-Advertisement-
Play Games

一、簡單介紹 Quartz.NET是一個強大、開源、輕量的作業調度框架,是 OpenSymphony 的 Quartz API 的.NET移植,用C#改寫,可用於WinForm和ASP.NET應用中。它靈活而不複雜,可以為執行一個作業而創建簡單或複雜的作業調度。它有很多特征,如:資料庫支持、集群、插 ...


    一、簡單介紹

    Quartz.NET是一個強大、開源、輕量的作業調度框架,是 OpenSymphony 的 Quartz API 的.NET移植,用C#改寫,可用於WinForm和ASP.NET應用中。它靈活而不複雜,可以為執行一個作業而創建簡單或複雜的作業調度。它有很多特征,如:資料庫支持、集群、插件、支持cron-like表達式等等。

    官網:http://www.quartz-scheduler.net/

    源碼:https://github.com/quartznet/quartznet

    示例:http://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html

    二、概念解釋

    Scheduler:作業調度器。

    IJob:作業介面,繼承並實現Execute, 編寫執行的具體作業邏輯。

    JobBuilder:根據設置,生成一個詳細作業信息(JobDetail)。

    TriggerBuilder:根據規則,生產對應的Trigger。

    三、示常式序

    3.1、界面

    新建一個WinForm程式Client,項目右鍵->屬性->應用程式->輸出類型,選擇控制台應用程式。

    3.2、引用

    項目右鍵->管理 NuGet 程式包->Quartz.NET。

    3.2、作業

    新建一個類DataSyncJob並繼承IJob,代表它是一個作業,同時實現Execute方法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Quartz;

namespace LinkTo.Test.Quartz.Client
{
    //打上DisallowConcurrentExecution標簽讓Job進行單線程跑,避免沒跑完時的重覆執行。
    [DisallowConcurrentExecution]
    public class DataSyncJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            JobDataMap keyValuePairs = context.MergedJobDataMap;
            if (keyValuePairs.Count > 0 && keyValuePairs.Contains("Hello"))
            {
                string value = context.MergedJobDataMap.Get("Hello").ToString();
                return Task.Run(() =>
                {
                    Console.WriteLine(DateTime.Now + $" Hello:{value}" + Environment.NewLine);
                });
            }
            else
            {
                return Task.Run(() =>
                {
                    Console.WriteLine(DateTime.Now + Environment.NewLine);
                });
            }
        }
    }
}
View Code

    說明:

    1)一般來說,作業需打上[DisallowConcurrentExecution]標簽,以避免當次作業尚未完成時又被開始調度執行。

    2)作業可以接收觸發器傳遞過來的參數(Key-Value),上面作業接收的是"Hello"參數。

    3.3、調度

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Quartz;
using Quartz.Impl;

namespace LinkTo.Test.Quartz.Client
{
    public partial class Main : Form
    {
        //調度器工廠
        private ISchedulerFactory factory;
        //調度器
        private IScheduler scheduler;

        public Main()
        {
            InitializeComponent();
            //按鈕狀態
            btnStart.Enabled = true;
            btnStop.Enabled = false;
        }

        /// <summary>
        /// 開始
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnStart_Click(object sender, EventArgs e)
        {
            //1、創建一個調度器
            factory = new StdSchedulerFactory();
            scheduler = await factory.GetScheduler();
            await scheduler.Start();

            //2、創建一個任務
            IJobDetail job = JobBuilder.Create<DataSyncJob>().WithIdentity("DataSync", "DataSync_Group").Build();

            //3、創建一個觸發器(有4種觸發器供選擇)
            //4、將任務與觸發器添加到調度器中

            #region 觸發器1:WithSimpleSchedule
            //ITrigger simpleTrigger = TriggerBuilder.Create()
            //    .WithIdentity("DataSync_SimpleTrigger", "DataSync_TriggerGroup")
            //    .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
            //    .Build();
            //await scheduler.ScheduleJob(job, simpleTrigger);
            #endregion

            #region 觸發器2:WithDailyTimeIntervalSchedule
            //ITrigger dailyTimeTrigger = TriggerBuilder.Create()
            //    .WithIdentity("DataSync_DailyTimeTrigger", "DataSync_TriggerGroup")
            //    .WithDailyTimeIntervalSchedule(x => x.OnEveryDay().WithIntervalInSeconds(5))
            //    .Build();
            //await scheduler.ScheduleJob(job, dailyTimeTrigger);
            #endregion

            #region 觸發器3:WithCalendarIntervalSchedule
            //ITrigger calendarTrigger = TriggerBuilder.Create()
            //    .WithIdentity("DataSync_CalendarTrigger", "DataSync_TriggerGroup")
            //    .WithCalendarIntervalSchedule(x => x.WithIntervalInSeconds(5))
            //    .Build();
            //await scheduler.ScheduleJob(job, calendarTrigger);
            #endregion

            #region 觸發器4:WithCronSchedule(帶傳遞參數"Hello")
            ITrigger cronTrigger = TriggerBuilder.Create()
                .WithIdentity("DataSync_CronTrigger", "DataSync_TriggerGroup")
                .WithCronSchedule("0/5 * * * * ?")
                .UsingJobData("Hello", Guid.NewGuid().ToString())
                .Build();
            await scheduler.ScheduleJob(job, cronTrigger);
            #endregion

            //5、開始執行
            await scheduler.Start();

            //按鈕狀態
            btnStart.Enabled = false;
            btnStop.Enabled = true;
        }

        /// <summary>
        /// 停止
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStop_Click(object sender, EventArgs e)
        {
            if (scheduler != null)
            {
                scheduler.Shutdown(true);
            }
            //按鈕狀態
            btnStart.Enabled = true;
            btnStop.Enabled = false;
        }
    }
}
View Code

    說明:

    1)Job及Trigger在WithIdentity時都會用到**Group,此處Group的作用是用於分類,相當於一個命名空間。

    2)觸發器有4種,分別是WithSimpleSchedule、WithDailyTimeIntervalSchedule、WithCalendarIntervalSchedule、WithCronSchedule,常用第1種及第4種。

    3)WithCronSchedule觸發器使用的是Cron表達式。

    3.4、結果

    四、Cron表達式

    1)線上Cron表達式生成器:https://cron.qqe2.com/

    2)官方英文介紹:https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/crontrigger.html

    3)Cron表達式整體上還是比較容易理解的,只有一點需要註意"?"號的用法。"?"可以用在 Day of Month 和 Day of Week 中,比如:每月1號的每小時的第31分鐘,正確的表達式是:* 31 * 1 * ?,而不能是:* 31 * 1 * *,因為這樣代表的是每周的任意一天。

Cron表達式由7段構成:秒 分 時 日 月 星期 年(可選)

"-":表示範圍,MON-WED表示星期一到星期三。
",":表示列舉,MON,WEB表示星期一和星期三。
"*":表示"每",每天、每月、每周、每年等。
"/":表示增量,0/15(分鐘)表示每15分鐘,在0分以後開始;3/20表示每20分鐘,從3分鐘以後開始。
"?":只能出現在日、星期段裡面,表示不指定具體的值。
"L":只能出現在日、星期段裡面,是Last的縮寫,表示如一個月的最後一天、一個星期的最後一天(星期六)。
"W":表示工作日,距離給定值最近的工作日。
"#":表示一個月的第幾個星期幾,"6#3"表示每個月的第三個星期五(1=SUN...6=FRI,7=SAT)。

    4)官方示例:

表達式解釋
0 0 12 * * ? 每天中午12點觸發
0 15 10 ? * * 每天上午10:15觸發
0 15 10 * * ? 每天上午10:15觸發
0 15 10 * * ? * 每天上午10:15觸發
0 15 10 * * ? 2005 2005年的每天上午10:15觸發
0 * 14 * * ? 在每天下午2點到下午2:59期間的每1分鐘觸發
0 0/5 14 * * ? 在每天下午2點到下午2:55期間的每5分鐘觸發
0 0/5 14,18 * * ? 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發
0 0-5 14 * * ? 在每天下午2點到下午2:05期間的每1分鐘觸發
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44觸發
0 15 10 ? * MON-FRI 周一至周五的上午10:15觸發
0 15 10 15 * ? 每月15日上午10:15觸發
0 15 10 L * ? 每月最後一日的上午10:15觸發
0 15 10 L-2 * ? 每個月的第二天到最後一天的上午10:15觸發
0 15 10 ? * 6L 每月的最後一個星期五上午10:15觸發
0 15 10 ? * 6L 每個月最後一個星期五上午10時15分觸發
0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最後一個星期五上午10:15觸發
0 15 10 ? * 6#3 每月的第三個星期五上午10:15觸發
0 0 12 1/5 * ? 每月每隔5天下午12點(中午)觸發, 從每月的第一天開始
0 11 11 11 11 ? 每11月11日上午11時11分觸發

    五、一行代碼實現調度

    新建一個TestJob:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Quartz;

namespace LinkTo.Test.Quartz.Client
{
    //打上DisallowConcurrentExecution標簽讓Job進行單線程跑,避免沒跑完時的重覆執行。
    [DisallowConcurrentExecution]
    public class TestJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            return Task.Run(() =>
            {
                Console.WriteLine(DateTime.Now + Environment.NewLine);
            });
        }
    }
}
TestJob.cs

    新建一個調度封裝類QuartzFactory:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Triggers;

namespace LinkTo.Test.Quartz.Client
{
    public class QuartzFactory
    {
        //調度器工廠
        private static ISchedulerFactory factory = null;
        //調度器
        private static IScheduler scheduler = null;

        /// <summary>
        /// 構造函數
        /// </summary>
        static QuartzFactory()
        {
            factory = new StdSchedulerFactory();
            scheduler = factory.GetScheduler().Result;
            scheduler.Start();
        }

        #region 觸發器1:添加Job
        /// <summary>
        /// 觸發器1:添加Job並以周期的形式運行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="startTime"></param>
        /// <param name="simpleTime"></param>
        /// <param name="jobDataMap"></param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, TimeSpan simpleTime, Dictionary<string, object> jobDataMap) where T : IJob
        {
            IJobDetail jobCheck = JobBuilder.Create<T>().WithIdentity(jobName, jobName + "_Group").Build();
            jobCheck.JobDataMap.PutAll(jobDataMap);
            ISimpleTrigger triggerCheck = new SimpleTriggerImpl(jobName + "_SimpleTrigger", 
                jobName + "_TriggerGroup",
                startTime,
                null,
                SimpleTriggerImpl.RepeatIndefinitely,
                simpleTime);
            return scheduler.ScheduleJob(jobCheck, triggerCheck).Result;
        }

        /// <summary>
        /// 觸發器1:添加Job並以周期的形式運行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="startTime"></param>
        /// <param name="simpleTime">毫秒數</param>
        /// <param name="mapKey"></param>
        /// <param name="mapValue"></param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, int simpleTime, string mapKey, object mapValue) where T : IJob
        {
            Dictionary<string, object> jobDataMap = new Dictionary<string, object>
            {
                { mapKey, mapValue }
            };
            return AddJob<T>(jobName, startTime, TimeSpan.FromMilliseconds(simpleTime), jobDataMap);
        }

        /// <summary>
        /// 觸發器1:添加Job並以周期的形式運行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="startTime"></param>
        /// <param name="simpleTime"></param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, TimeSpan simpleTime) where T : IJob
        {
            return AddJob<T>(jobName, startTime, simpleTime, new Dictionary<string, object>());
        }

        /// <summary>
        /// 觸發器1:添加Job並以周期的形式運行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="startTime"></param>
        /// <param name="simpleTime">毫秒數</param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, int simpleTime) where T : IJob
        {
            return AddJob<T>(jobName, startTime, TimeSpan.FromMilliseconds(simpleTime));
        }

        /// <summary>
        /// 觸發器1:添加Job並以周期的形式運行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="simpleTime">毫秒數</param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, int simpleTime) where T : IJob
        {
            return AddJob<T>(jobName, DateTime.UtcNow.AddMilliseconds(1), TimeSpan.FromMilliseconds(simpleTime));
        }
        #endregion

        #region 觸發器4:添加Job
        /// <summary>
        /// 觸發器4:添加Job並以定點的形式運行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="cronTime"></param>
        /// <param name="jobDataMap"></param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, string cronTime, string jobData) where T : IJob
        {
            IJobDetail jobCheck = JobBuilder.Create<T>().WithIdentity(jobName, jobName + "_Group").UsingJobData("jobData", jobData).Build();
            ICronTrigger cronTrigger = new CronTriggerImpl(jobName + "_CronTrigger", jobName + "_TriggerGroup", cronTime);
            return scheduler.ScheduleJob(jobCheck, cronTrigger).Result;
        }

        /// <summary>
        /// 觸發器4:添加Job並以定點的形式運行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="cronTime"></param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, string cronTime) where T : IJob
        {
            return AddJob<T>(jobName, cronTime, null);
        }
        #endregion

        /// <summary>
        /// 修改觸發器時間重載
        /// </summary>
        /// <param name="jobName">Job名稱</param>
        /// <param name="timeSpan">TimeSpan</param>
        /// </summary>
        public static void UpdateTime(string jobName, TimeSpan simpleTimeSpan)
        {
            TriggerKey triggerKey = new TriggerKey(jobName + "_SimpleTrigger", jobName + "_TriggerGroup");
            SimpleTriggerImpl simpleTriggerImpl = scheduler.GetTrigger(triggerKey).Result as SimpleTriggerImpl;
            simpleTriggerImpl.RepeatInterval = simpleTimeSpan;
            scheduler.RescheduleJob(triggerKey, simpleTriggerImpl);
        }

        /// <summary>
        /// 修改觸發器時間重載
        /// </summary>
        /// <param name="jobName">Job名稱</param>
        /// <param name="simpleTime">分鐘數</param>
        /// <summary>
        public static void UpdateTime(string jobName, int simpleTime)
        {
            UpdateTime(jobName, TimeSpan.FromMinutes(simpleTime));
        }

        /// <summary>
        /// 修改觸發器時間重載
        /// </summary>
        /// <param name="jobName">Job名稱</param>
        /// <param name="cronTime">Cron表達式</param>
        public static void UpdateTime(string jobName, string cronTime)
        {
            TriggerKey triggerKey = new TriggerKey(jobName + "_CronTrigger", jobName + "_TriggerGroup");
            CronTriggerImpl cronTriggerImpl = scheduler.GetTrigger(triggerKey).Result as CronTriggerImpl;
            cronTriggerImpl.CronExpression = new CronExpression(cronTime);
            scheduler.RescheduleJob(triggerKey, cronTriggerImpl);
        }

        /// <summary>
        /// 暫停所有Job
        /// </summary>
        public static void PauseAll()
        {
            scheduler.PauseAll();
        }

        /// <summary>
        /// 恢復所有Job
        /// </summary>
        public static void ResumeAll()
        {
            scheduler.ResumeAll();
        }

        /// <summary>
        /// 刪除指定Job
        /// </summary>
        /// <param name="jobName"></param>
        public static void DeleteJob(string jobName)
        {
            JobKey jobKey = new JobKey(jobName, jobName + "_Group");
            scheduler.DeleteJob(jobKey);
        }

        /// <summary>
        /// 卸載定時器
        /// </summary>
        /// <param name="isWaitForToComplete">是否等待Job執行完成</param>
        public static void Shutdown(bool isWaitForToComplete)
        {
            scheduler.Shutdown(isWaitForToComplete);
        }
    }
}
QuartzFactory.cs

    一行代碼實現作業調度按鈕代碼:

        /// <summary>
        /// 一行代碼實現作業調度
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOneCode_Click(object sender, EventArgs e)
        {
            if (btnOneCode.Text == "一行代碼實現作業調度")
            {
                string cronTime = "0/5 * * * * ?";
                QuartzFactory.AddJob<TestJob>("TestJob", cronTime);
                btnOneCode.Text = "Stop";
            }
            else
            {
                QuartzFactory.DeleteJob("TestJob");
                btnOneCode.Text = "一行代碼實現作業調度";
            }
        }
btnOneCode_Click

 

    參考自:

    https://www.cnblogs.com/best/p/7658573.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...