RDIFramework.NET框架基於Quartz.Net實現任務調度詳解及效果展現

在上一篇Quartz.Net實現做業定時調度詳解,咱們經過實例代碼詳細講解與演示了基於Quartz.NET開發的詳細方法。本篇咱們主要講述基於RDIFramework.NET框架整合Quartz.NET,以實現任務調度,並對任務持久化操做的全過程。本文主要經過如下幾個方面講解:數據庫

  • 一、任務調度概述
  • 2 任務調度管理
    • 2.一、Cron表達式
    • 2.二、建立用戶過程調度任務
    • 2.三、建立程序集任務

一、任務調度概述

任務調度在各類應用中都會存在,在業務系統中咱們爲了調度一些自動執行的任務或從隊列中消費一些消息,因此基本上都會涉及到後臺服務的開發。在瞭解任務調度以前,咱們先了解一下實現任務調度的Quartz.NET框架。編程

Quartz.NET是一個開源的做業調度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#寫成,可用於winform和asp.net應用中。它提供了巨大的靈活性而不犧牲簡單性。你可以用它來爲執行一個做業而建立簡單的或複雜的調度。它有不少特徵,如:數據庫支持,集羣,插件,支持cron-like表達式等等。你曾經須要應用執行一個任務嗎?這個任務天天或每週星期二晚上11:30,或許僅僅每月的最後一天執行。一個自動執行而無須干預的任務在執行過程當中若是發生一個嚴重錯誤,應用可以知到其執行失敗並嘗試從新執行嗎?你和你的團隊是用.NET編程嗎?若是這些問題中任何一個你回答是,那麼你應該使用Quartz.NET調度器。 Quartz.NET容許開發人員根據時間間隔(或天)來調度做業。它實現了做業和觸發器的多對多關係,還能把多個做業與不一樣的觸發器關聯。整合了 Quartz.NET的應用程序能夠重用來自不一樣事件的做業,還能夠爲一個事件組合多個做業。api

總的來講就是Quartz.NET是一個開源的做業調度框架,很是適合在平時的工做中,定時輪詢數據庫同步,定時郵件通知,定時處理數據等。 Quartz.NET容許開發人員根據時間間隔(或天)來調度做業。它實現了做業和觸發器的多對多關係,還能把多個做業與不一樣的觸發器關聯,配置靈活方便。至關於數據庫中的 Job、Windows 的計劃任務、Unix/Linux 下的 Cron,但 Quartz 能夠把排程控制的更精細,對任務調度的領域問題進行了高度的抽象,實現做業的靈活調度。微信

咱們框架的任務調度就是基於Quartz.NET框架的整合使用,並對任務作了持久化的操做。微信開發

二、任務調度管理

「任務列表」管理模塊是放在「系統配置」->「任務調度」下的「任務列表」,任務列表以下圖所示。任務列表主界面左側顯示的是已經建立的任務,右側爲當前選中任務的執行狀況列表。在左側的任務列表最左邊的操做欄,能夠經過操做按鈕對當前任務作刪除、暫停、啓動、刪除任務日誌的操做。app

在任務列表主界面頂部的工具欄中的按鈕可用來建立任務,建立的任務分爲兩種類型:框架

  1. 建立用戶過程調度任務。asp.net

  2. 建立程序集任務。jsp

每種類型下的任務又能夠分爲簡單任務與複雜任務。簡單任務相似於定時器每隔特定的間隔時間觸發,複雜任務主要是主要是藉助CronTrigger表達式來實現相似數據庫中的計劃任務類型的工做。使用CronTrigger你能夠指定諸如「每一個週五中午」,或者「每一個工做日的9:30」或者「從每一個周1、周3、週五的上午9:00到上午10:00之間每隔五分鐘」這樣日程安排來觸發。甚至像SimpleTrigger(簡單任務)同樣,CronTrigger也有一個StartTime以指定日程從何時開始,也有一個(可選的)EndTime以指定什麼時候日程再也不繼續。
下面的章節咱們分別對這兩種任務類型作介紹。在介紹以前先了解學習一下Cron 表達式。分佈式

2.一、Quartz的cron表達式

Cron表達式是一個字符串,字符串以5或6個空格隔開,分爲6或7個域,每個域表明一個含義,Cron有以下兩種語法格式:

(1) Seconds Minutes Hours DayofMonth Month DayofWeek Year

(2)Seconds Minutes Hours DayofMonth Month DayofWeek

1、結構

  corn從左到右(用空格隔開):秒 分 小時 月份中的日期 月份 星期中的日期 年份

2、各字段的含義

  注意:每個域都使用數字,但還能夠出現以下特殊字符,它們的含義是:

  (1):表示匹配該域的任意值。假如在Minutes域使用, 即表示每分鐘都會觸發事件。

  (2)?:只能用在DayofMonth和DayofWeek兩個域。它也匹配域的任意值,但實際不會。由於DayofMonth和DayofWeek會相互影響。例如想在每個月的20日觸發調度,無論20日究竟是星期幾,則只能使用以下寫法: 13 13 15 20 * ?, 其中最後一位只能用?,而不能使用,若是使用表示無論星期幾都會觸發,實際上並非這樣。

  (3)-:表示範圍。例如在Minutes域使用5-20,表示從5分到20分鐘每分鐘觸發一次

  (4)/:表示起始時間開始觸發,而後每隔固定時間觸發一次。例如在Minutes域使用5/20,則意味着5分鐘觸發一次,而25,45等分別觸發一次.

  (5),:表示列出枚舉值。例如:在Minutes域使用5,20,則意味着在5和20分每分鐘觸發一次。

  (6)L:表示最後,只能出如今DayofWeek和DayofMonth域。若是在DayofWeek域使用5L,意味着在最後的一個星期四觸發。

  (7)W:表示有效工做日(週一到週五),只能出如今DayofMonth域,系統將在離指定日期的最近的有效工做日觸發事件。例如:在 DayofMonth使用5W,若是5日是星期六,則將在最近的工做日:星期五,即4日觸發。若是5日是星期天,則在6日(週一)觸發;若是5日在星期一到星期五中的一天,則就在5日觸發。另一點,W的最近尋找不會跨過月份 。

  (8)LW:這兩個字符能夠連用,表示在某個月最後一個工做日,即最後一個星期五。

  (9)#:用於肯定每月第幾個星期幾,只能出如今DayofMonth域。例如在4#2,表示某月的第二個星期三。

3、經常使用表達式例子

  (1)0 0 2 1 * ? * 表示在每個月的1日的凌晨2點調整任務

  (2)0 15 10 ? * MON-FRI 表示週一到週五天天上午10:15執行做業

  (3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每月的最後一個星期五上午10:15執行做

  (4)0 0 10,14,16 * * ? 天天上午10點,下午2點,4點

  (5)0 0/30 9-17 * * ? 朝九晚五工做時間內每半小時

  (6)0 0 12 ? * WED 表示每一個星期三中午12點

  (7)0 0 12 * * ? 天天中午12點觸發

  (8)0 15 10 ? * * 天天上午10:15觸發

  (9)0 15 10 * * ? 天天上午10:15觸發

  (10)0 15 10 * * ? * 天天上午10:15觸發

  (11)0 15 10 * * ? 2005 2005年的天天上午10:15觸發

  (12)0 * 14 * * ? 在天天下午2點到下午2:59期間的每1分鐘觸發

  (13)0 0/5 14 * * ? 在天天下午2點到下午2:55期間的每5分鐘觸發

  (14)0 0/5 14,18 * * ? 在天天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發

  (15)0 0-5 14 * * ? 在天天下午2點到下午2:05期間的每1分鐘觸發

  (16)0 10,44 14 ? 3 WED 每一年三月的星期三的下午2:10和2:44觸發

  (17)0 15 10 ? * MON-FRI 週一至週五的上午10:15觸發

  (18)0 15 10 15 * ? 每個月15日上午10:15觸發

  (19)0 15 10 L * ? 每個月最後一日的上午10:15觸發

  (20)0 15 10 ? * 6L 每個月的最後一個星期五上午10:15觸發

  (21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每個月的最後一個星期五上午10:15觸發

  (22)0 15 10 ? * 6#3 每個月的第三個星期五上午10:15觸發

注:

  (1)有些子表達式能包含一些範圍或列表

  例如:

  子表達式(天(星期))能夠爲 「MON-FRI」,「MON,WED,FRI」,「MON-WED,SAT」

  「*」字符表明全部可能的值,

  所以,「」在子表達式(月)裏表示每月的含義,「」在子表達式(天(星期))表示星期的每一天

  「/」字符用來指定數值的增量

  例如:在子表達式(分鐘)裏的「0/15」表示從第0分鐘開始,每15分鐘

  在子表達式(分鐘)裏的「3/20」表示從第3分鐘開始,每20分鐘(它和「3,23,43」)的含義同樣

  「?」字符僅被用於天(月)和天(星期)兩個子表達式,表示不指定值

  當2個子表達式其中之一被指定了值之後,爲了不衝突,須要將另外一個子表達式的值設爲「?」

  「L」 字符僅被用於天(月)和天(星期)兩個子表達式,它是單詞「last」的縮寫

  可是它在兩個子表達式裏的含義是不一樣的。

  在天(月)子表達式中,「L」表示一個月的最後一天

  在天(星期)自表達式中,「L」表示一個星期的最後一天,也就是SAT

  若是在「L」前有具體的內容,它就具備其餘的含義了

  例如:「6L」表示這個月的倒數第6天,「FRIL」表示這個月的最一個星期五

  注意:在使用「L」參數時,不要指定列表或範圍,由於這會致使問題

4、表達式生成器

有不少的cron表達式在線生成器,這裏給你們推薦幾款

http://www.pdtools.net/tools/becron.jsp

或者

http://cron.qqe2.com/

2.二、建立用戶過程調度任務

過程調度任務簡單的理解就是能夠執行SQL語句或存儲過程等。建立用戶過程調度任務以下圖所示。

在建立用戶過程調試界面,「過程SQL」就是執行的SQL語句或存儲過程或函數等。過程參數就是過程SQL中的參數列表對應的參數值。建立的任務默認是簡單任務,如上圖咱們建立了一個每1分鐘執行一次的簡單過程任務,其實「無限次」選中就表示不限次數,不然能夠指定執行的次數。要建立複雜任務能夠單擊「複雜任務」選項卡,以下圖所示。

複雜任務中的各時間項的配置就是Cron表達式,每單擊一個配置項,右側都對該配置項進行了詳細的設置說明。設置好後能夠單擊「檢查表達式」來驗證Cron表達式的正確性,以下圖所示。

單擊確認按鈕便可成功建立任務。要刪除、暫停、重啓、刪除任務日誌,只需選中任務後單擊當前任務左側的操做按鈕區域對應的操做按鈕便可,以下圖所示。

2.三、建立程序集任務

程序集任務簡單的理解就是建立一個自動執行的C#方法,程序集任務與用戶過程調度任務相似,也分簡單的任務與複雜的任務,建立程序集任務以下圖所示。

咱們在微信公衆號開發系列-玩轉微信開發-目錄彙總系列文章中對微信開發進行了詳細的講解,咱們知道微信提供的API大多都是以微信分配給咱們的一個access_token爲基礎,Access Token至關於打開這些服務的鑰匙,正常狀況下會在7200秒內失效。對於access_tokenr的詳細介紹可參考咱們的:微信公衆號開發系列-四、獲取接口調用憑證

前面的開發咱們都是失效後各自應用去從新獲取access_token。雖然這樣操做可行但不是理想的操做方式,理想的操做方式應該是後臺定時自動刷新咱們獲得的access_token。咱們可使用任務調度來實現access_token的獲取。

在上圖中咱們建立了一個每30分鐘自動更新微信公衆號開發中的access_token,配置項的中程序集名稱格式爲:命名空間+類,具體的開發方法能夠參考咱們任務調度中的Job事例。編寫的job只須要繼承咱們的基類:ITaskJob,並實現如下方法便可。

一、public string RunJob(ref JobDataMap dataMap, string jobName, string id, string taskName)

二、public string RunJobBefore(JobEntity jobModel)

三、public string CloseJob(JobEntity jobModel)

參考代碼以下:

public class WeChatGetTokenJob : ITaskJob
{
    public string RunJob(ref JobDataMap dataMap, string jobName, string id, string taskName)
    {
        int returnValue = 0;
        List<KeyValuePair<string, object>> parmeters = new List<KeyValuePair<string, object>>
            {
                new KeyValuePair<string, object>(WeixinOfficialAccountTable.FieldDeleteMark, 0)
            };
        var listOfficialAccount = BaseEntity.GetList<WeixinOfficialAccountEntity>(RDIFrameworkService.Instance.WeixinBasicService.GetOfficialAccountDTByValues(parmeters));
        if (listOfficialAccount != null && listOfficialAccount.Count() > 0)
        {
            foreach (WeixinOfficialAccountEntity entity in listOfficialAccount)
            {
                try
                {
                    if (entity.Category == (int)WeChatSubscriberEnum.EnterpriseSubscriber)
                    {
                        if (!string.IsNullOrEmpty(entity.AppId) && !string.IsNullOrEmpty(entity.AppSecret))
                        {
                            //方法一:使用Senparc.WeiXin SDK的方法
                            entity.AccessToken = Senparc.Weixin.QY.CommonAPIs.CommonApi.GetToken(entity.AppId, entity.AppSecret).access_token;
                            entity.ModifiedBy = "job_rdiframework";
                            //方式二,直接調用微信的接口方法
                            //var url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type={0}&appid={1}&secret={2}", "client_credential".AsUrlData(), entity.AppId.AsUrlData(), entity.AppSecret.AsUrlData());
                            //AccessTokenResult result = Get.GetJson<AccessTokenResult>(url);
                            //entity.AccessToken = result.access_token;

                            entity.ModifiedOn = DateTime.Now;
                            returnValue += RDIFrameworkService.Instance.WeixinBasicService.UpdateOfficialAccount(entity);
                        }
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(entity.AppId) && !string.IsNullOrEmpty(entity.AppSecret))
                        {
                            //方法一:使用Senparc.WeiXin SDK的方法
                            entity.AccessToken = Senparc.Weixin.MP.CommonAPIs.CommonApi.GetToken(entity.AppId, entity.AppSecret).access_token;
                            entity.ModifiedBy = "job_rdiframework";
                            //方式二,直接調用微信的接口方法
                            //var url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type={0}&appid={1}&secret={2}", "client_credential".AsUrlData(), entity.AppId.AsUrlData(), entity.AppSecret.AsUrlData());
                            //AccessTokenResult result = Get.GetJson<AccessTokenResult>(url);
                            //entity.AccessToken = result.access_token; 
                            entity.ModifiedOn = DateTime.Now;
                            returnValue += RDIFrameworkService.Instance.WeixinBasicService.UpdateOfficialAccount(entity);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.WriteException(ex);
                }
            }
        }

        if (returnValue > 0)
        {
            TaskJob.UpdateState(jobName, 1, "成功");               
        }

        return "批量更新Access_Token!";
    }

    public string RunJobBefore(JobEntity jobModel)
    {
        Log.Write("RunJobBefor", jobModel.taskName,"運行");
        List<KeyValuePair<string, object>> parmeters = new List<KeyValuePair<string, object>>
            {
                new KeyValuePair<string, object>(WeixinOfficialAccountTable.FieldDeleteMark, 0)
            };
        var listOfficialAccount = BaseEntity.GetList<WeixinOfficialAccountEntity>(RDIFrameworkService.Instance.WeixinBasicService.GetOfficialAccountDTByValues(parmeters));


        if (listOfficialAccount == null || listOfficialAccount.Count() <= 0)
        {
            return "沒有符合獲取Access_Token的數據!";
        }

        return null;
    }


    public string CloseJob(JobEntity jobModel)
    {
        Log.Write("CloseJob", jobModel.taskName,"關閉");
        TaskJob.UpdateState(jobModel.id, 3, "掛起");
        return "關閉獲取Access_Token任務";
    }
}

三、相關文章參考


一路走來數個年頭,感謝RDIFramework.NET框架的支持者與使用者,你們能夠經過下面的地址瞭解詳情。

RDIFramework.NET官方網站:http://www.rdiframework.net/

RDIFramework.NET官方博客:http://blog.rdiframework.net/

同時須要說明的,之後的全部技術文章以官方網站爲準,歡迎你們收藏!

RDIFramework.NET框架由專業團隊長期打造、一直在更新、一直在升級,請放心使用!

歡迎關注RDIFramework.net框架官方公衆微信(微信號:guosisoft),及時瞭解最新動態。

掃描二維碼當即關注

微信號:guosisoft

相關文章
相關標籤/搜索