進來項目中用到了 任務調度 來實現短信發送網關,因此這裏分享下 Quartz.net 的使用經驗。 任務調度選用了 Quartz.net 來實現,以前用的是C#中自帶的window服務安裝,此次也打算用Topshel來作window服務,其實感受差很少。ok,接下來講下 Quartz.net 在開發中的使用:html
Quartz.NET簡介數據庫
做業調度的目標在於按照預先肯定的時間和指定的順序來確保高效的數據處理流程,從而最大限度的使用系統資源。批處理流程是一種在無需最終用戶干預的方式下在後臺經過順序方式運行的操做。express
Quartz.NET是一個開源的做業調度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#寫成,可用於winform和asp.net應用中。它提供了巨大的靈活性而不犧牲簡單性。你可以用它來爲執行一個做業而建立簡單的或複雜的調度。它有不少特徵,如:數據庫支持,集羣,插件,支持cron-like表達式等等。編程
Quartz.NET 是一個開源的做業調度框架,它能夠被在小型應用程序甚至是企業級的應用程序。它有以下幾個特色:app
Quartz.NET使用框架
首先引用下面的dllasp.net
前4個是Quartz.NET使用所必須的,Toshelf是用來作window服務所用的ide
添加QuartzHelp類庫學習
添加JobDemo.cs,實現IJob接口。測試
1 namespace QuartzHelp 2 { 3 public class JobDemo : IJob 4 { 5 //日誌對象 6 private static readonly ILog logger = LogManager.GetLogger(typeof(JobDemo)); 7 8 public void Execute(IJobExecutionContext context) 9 { 10 logger.Info("JobDemo開始運做,模擬處理200ms的程序"); 11 Thread.Sleep(200); 12 logger.Info("JobDemo處理完"); 13 } 14 } 15 }
添加 WindowControl 控制檯
添加Service.cs文件,做爲任務調度的入口
1 namespace WindowControl 2 { 3 public class Service 4 { 5 private readonly ILog logger; 6 private IScheduler scheduler; 7 public Service() 8 { 9 logger = LogManager.GetLogger(typeof(Service)); 10 ISchedulerFactory schedulerFactory = new StdSchedulerFactory(); 11 scheduler = schedulerFactory.GetScheduler(); 12 } 13 14 public void Start() 15 { 16 scheduler.Start(); 17 logger.Info("Quartz服務成功啓動"); 18 } 19 20 public void Stop() 21 { 22 scheduler.Shutdown(true); 23 logger.Info("Quartz服務成功終止"); 24 } 25 26 } 27 }
在Program.cs文件中,啓動任務調度
1 namespace WindowControl 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 HostFactory.Run(x => 8 { 9 x.Service<Service>((s) => 10 { 11 s.SetServiceName("ser"); 12 s.ConstructUsing(name => new Service()); 13 s.WhenStarted((t) => t.Start()); 14 s.WhenStopped((t) => t.Stop()); 15 }); 16 17 x.RunAsLocalSystem(); 18 19 //服務的描述 20 x.SetDescription("任務服務安裝測試"); 21 //服務的顯示名稱 22 x.SetDisplayName("MyDisplayName"); 23 //服務名稱 24 x.SetServiceName("MyServiceName"); 25 26 }); 27 } 28 } 29 }
添加配置文件
新建一個Configs的文件夾,首先添加quartz_jobs.xml(做爲 調度任務 的配置文件)
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <!-- This file contains job definitions in schema version 2.0 format --> 4 5 <job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"> 6 7 <processing-directives> 8 <!-- 在計劃做業和觸發器是應遵循的命令和原則 --> 9 <overwrite-existing-data>true</overwrite-existing-data> 10 </processing-directives> 11 12 <schedule> 13 <job> 14 <name>myJob</name> 15 <group>myJobGroup</group> 16 <description>第一個工做任務</description> 17 <job-type>QuartzHelp.JobDemo, QuartzHelp</job-type> 18 <durable>true</durable> 19 <recover>false</recover> 20 </job> 21 <trigger> 22 <cron> 23 <name>Trigger</name> 24 <group>TriggerGroup</group> 25 <description>Simple trigger to simply fire sample job</description> 26 <job-name>myJob</job-name> 27 <job-group>myJobGroup</job-group> 28 <!--每10秒中執行一次--> 29 <cron-expression>0/01 * * * * ?</cron-expression> 30 </cron> 31 </trigger> 32 </schedule> 33 </job-scheduling-data>
quartz_jobs.xml配置項說明:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <!-- This file contains job definitions in schema version 2.0 format --> 3 4 <!--在2.0版本中根結點由<quartz> 變爲了<job-scheduling-data>--> 5 <job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"> 6 7 <!--保持默認配置便可,具體做用有待研究--> 8 <processing-directives> 9 <overwrite-existing-data>true</overwrite-existing-data> 10 </processing-directives> 11 12 <!--任務調度集合--> 13 <schedule> 14 <!--2.0版本中的job至關於1.x版本中的<job-detail>,這個節點是用來定義每一個具體的任務的,多個任務請建立多個job節點便可--> 15 <job> 16 <!--任務名稱,同一個group中多個job的name不能相同,若未設置group則全部未設置group的job爲同一個分組(必須設置)--> 17 <name>sampleJob</name> 18 <!--任務所屬分組,用於標識任務所屬分組--> 19 <group>sampleGroup</group> 20 <!--工做任務的描述,用於描述任務具體內容--> 21 <description>Sample job for Quartz Server</description> 22 <!--任務類型,任務的具體類型及所屬程序集,格式:實現了IJob接口的包含完整命名空間的類名,程序集名稱--> 23 <job-type>Quartz.Server.SampleJob, Quartz.Server</job-type> 24 <!--<durable>(持久性)-若是一個Job是不持久的, 一旦沒有觸發器與之關聯,它就會被從scheduler 中自動刪除--> 25 <durable>true</durable> 26 <recover>false</recover> 27 28 </job> 29 <!--trigger 任務觸發器,用於定義使用何種方式觸發任務(job),同一個job能夠定義多個trigger ,多個trigger 各自獨立的執行調度,每一個trigger 中必須且只能定義一種觸發器類型(calendar-interval、simple、cron) 30 calendar-interval 一種觸發器類型,使用較少,此處略過--> 31 <trigger> 32 <!--簡單任務的觸發器,能夠調度用於重複執行的任務--> 33 <simple> 34 <!--觸發器名稱,同一個分組中的名稱必須不一樣--> 35 <name>sampleSimpleTrigger</name> 36 <!--觸發器組--> 37 <group>sampleSimpleGroup</group> 38 <!--觸發器描述--> 39 <description>Simple trigger to simply fire sample job</description> 40 <!--要調度的任務名稱,該job-name必須和對應job節點中的name徹底相同--> 41 <job-name>sampleJob</job-name> 42 <!--調度任務(job)所屬分組,該值必須和job中的group徹底相同--> 43 <job-group>sampleGroup</job-group> 44 <!--start-time(選填) 任務開始執行時間utc時間,北京時間須要+08:00,如:<start-time>2012-04-01T08:00:00+08:00</start-time>表示北京時間2012年4月1日上午8:00開始執行,注意服務啓動或重啓時都會檢測此屬性,若沒有設置此屬性或者start-time設置的時間比當前時間較早,則服務啓動後會當即執行一次調度,若設置的時間比當前時間晚,服務會等到設置時間相同後纔會第一次執行任務,通常若無特殊須要請不要設置此屬性--> 45 <misfire-instruction>SmartPolicy</misfire-instruction> 46 <!--任務執行次數,如:<repeat-count>-1</repeat-count>表示無限次執行--> 47 <repeat-count>-1</repeat-count> 48 <!--任務觸發間隔(毫秒)--> 49 <repeat-interval>10000</repeat-interval> 50 </simple> 51 </trigger> 52 <job> 53 <name>CommissionJob</name> 54 <group>CommissionJob</group> 55 <description>Sample job for Quartz Server</description> 56 <job-type>Settlement.Jobs.CommissionJob, Settlement.Jobs</job-type> 57 <durable>true</durable> 58 <recover>false</recover> 59 60 </job> 61 <trigger> 62 <!--cron複雜任務觸發器使用cron表達式定製任務調度--> 63 <cron> 64 <name>sampleSimpleTrigger2</name> 65 <group>sampleSimpleTrigger2</group> 66 <job-name>sampleJob2</job-name> 67 <job-group>sampleGroup2</job-group> 68 <!--start-time(選填) 任務開始執行時間utc時間,北京時間須要+08:00,如:<start-time>2012-04-01T08:00:00+08:00</start-time>表示北京時間2012年4月1日上午8:00開始執行,注意服務啓動或重啓時都會檢測此屬性,若沒有設置此屬性,服務會根據cron-expression的設置執行任務調度;若start-time設置的時間比當前時間較早,則服務啓動後會忽略掉cron-expression設置,當即執行一次調度,以後再根據cron-expression執行任務調度;若設置的時間比當前時間晚,則服務會在到達設置時間相同後纔會應用cron-expression,根據規則執行任務調度,通常若無特殊須要請不要設置此屬性--> 69 <!--cron表達式--> 70 <cron-expression>0/10 * * * * ?</cron-expression> 71 </cron> 72 </trigger> 73 </schedule> 74 </job-scheduling-data>
具體能夠參考:http://www.cnblogs.com/h20064528/archive/2012/07/17/2595636.html
Quartz的cron表達式
一個cron表達式有至少6個(也可能7個)有空格分隔的時間元素。按順序依次爲1.秒(0~59)2.分鐘(0~59)3.小時(0~23)4.天(月)(0~31,可是你須要考慮你月的天數)5.月(0~11)6.天(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)7.年份(1970-2099) 其中每一個元素能夠是一個值(如6),一個連續區間(9-12),一個間隔時間(8-18/4)(/表示每隔4小時),一個列表(1,3,5),通配符。因爲"月份中的日期"和"星期中的日期"這兩個元素互斥的,必需要對其中一個設置?. 0 0 10,14,16 * * ? 天天上午10點,下午2點,4點0 0/30 9-17 * * ??? 朝九晚五工做時間內每半小時0 0 12 ? * WED 表示每一個星期三中午12點 有些子表達式能包含一些範圍或列表例如:子表達式(天(星期))能夠爲 「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」參數時,不要指定列表或範圍,由於這會致使問題 ============================================ CronTrigger配置完整格式爲: [秒] [分] [小時] [日] [月] [周] [年] 序號 說明 是否必填 容許填寫的值 容許的通配符 1 秒 是 0-59 , - * / 2 分 是 0-59 , - * / 3 小時 是 0-23 , - * / 4 日 是 1-31 , - * ? / L W 5 月 是 1-12 or JAN-DEC , - * / 6 周 是 1-7 or SUN-SAT , - * ? / L # 7 年 否 empty 或 1970-2099 , - * / 通配符說明:*表示全部值. 例如:在分的字段上設置 "*",表示每一分鐘都會觸發。? 表示不指定值。使用的場景爲不須要關心當前設置這個字段的值。例如:要在每個月的10號觸發一個操做,但不關心是周幾,因此須要周位置的那個字段設置爲"?" 具體設置爲 0 0 0 10 * ?- 表示區間。例如 在小時上設置 "10-12",表示 10,11,12點都會觸發。, 表示指定多個值,例如在周字段上設置 "MON,WED,FRI" 表示週一,週三和週五觸發/用於遞增觸發。如在秒上面設置"5/15" 表示從5秒開始,每增15秒觸發(5,20,35,50)。 在月字段上設置'1/3'所示每個月1號開始,每隔三天觸發一次。L 表示最後的意思。在日字段設置上,表示當月的最後一天(依據當前月份,若是是二月還會依據是不是潤年[leap]), 在周字段上表示星期六,至關於"7"或"SAT"。若是在"L"前加上數字,則表示該數據的最後一個。例如在周字段上設置"6L"這樣的格式,則表示「本月最後一個星期五" W 表示離指定日期的最近那個工做日(週一至週五). 例如在日字段上設置"15W",表示離每個月15號最近的那個工做日觸發。若是15號正好是週六,則找最近的週五(14號)觸發, 若是15號是周未,則找最近的下週一(16號)觸發.若是15號正好在工做日(週一至週五),則就在該天觸發。若是指定格式爲 "1W",它則表示每個月1號日後最近的工做日觸發。若是1號正是週六,則將在3號下週一觸發。(注,"W"前只能設置具體的數字,不容許區間"-").# 序號(表示每個月的第幾個周幾),例如在周字段上設置"6#3"表示在每個月的第三個週六.注意若是指定"#5",正好第五週沒有周六,則不會觸發該配置(用在母親節和父親節再合適不過了) ;小提示:'L'和 'W'能夠一組合使用。若是在日字段上設置"LW",則表示在本月的最後一個工做日觸發;周字段的設置,若使用英文字母是不區分大小寫的,即MON與mon相同;經常使用示例: 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分每分觸發 0 0/5 14 * * ? 天天下午的 2點到2點59分(整點開始,每隔5分觸發) 0 0/5 14,18 * * ? 天天下午的 2點到2點59分、18點到18點59分(整點開始,每隔5分觸發) 0 0-5 14 * * ? 天天下午的 2點到2點05分每分觸發 0 10,44 14 ? 3 WED 3月分每週三下午的 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 ? * 6L 每個月最後一週的星期五的10點15分觸發 0 15 10 ? * 6L 2002-2005 從2002年到2005年每個月最後一週的星期五的10點15分觸發 0 15 10 ? * 6#3 每個月的第三週的星期五開始觸發 0 0 12 1/5 * ? 每個月的第一個中午開始每隔5天觸發一次 0 11 11 11 11 ? 每一年的11月11號 11點11分觸發(光棍節)
具體能夠參考:http://www.cnblogs.com/yaowen/p/3779284.html
而後配置app.config文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <configuration> 3 <configSections> 4 <section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089"/> 5 <sectionGroup name="common"> 6 <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"/> 7 </sectionGroup> 8 </configSections> 9 <common> 10 <logging> 11 <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net"> 12 <!-- inline :log4net 節點在App.Config/Web.Config 文件中配置 file:使用外置配置文件 (須要configFile參數配合使用,<arg key="configFile" value="外部配置文件") file-watch: 與"file"同樣,只是多了一個監視外部配置文件的變更功能,若是有變更則從新加載配置。 external:IBatis將不會嘗試配置Log4Net。 --> 13 <arg key="configType" value="file-watch"/> 14 <!-- 這裏必需要保證在生成的目錄下面有Configs這個文件夾--> 15 <arg key="configFile" value="~/Configs/quartz_log4net.config"/> 16 </factoryAdapter> 17 </logging> 18 </common> 19 <quartz> 20 <add key="quartz.scheduler.instanceName" value="TaskScheduler"/> 21 <add key="quartz.scheduler.instanceId" value="AUTO"/> 22 <add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz"/> 23 <add key="quartz.threadPool.threadCount" value="5"/> 24 <add key="quartz.threadPool.threadPriority" value="Normal"/> 25 <add key="quartz.plugin.xml.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz"/> 26 <!-- 這裏必需要保證在生成的目錄下面有Configs這個文件夾--> 27 <add key="quartz.plugin.xml.fileNames" value="~/Configs/quartz_jobs.config"/> 28 <add key="quartz.plugin.xml.scanInterval" value="600"/> 29 </quartz> 30 <startup> 31 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> 32 </startup> 33 </configuration>
最後,爲日記記錄配置quartz_log4net.config文件,固然,你也能夠不用獨立出來,直接配置在app.config文件中也是能夠的
1 <?xml version="1.0" encoding="utf-8"?> 2 <log4net> 3 <appender name="FileAppender" type="log4net.Appender.RollingFileAppender"> 4 <param name="File" value="Log\\" /> 5 <param name="AppendToFile" value="true" /> 6 <param name="MaxFileSize" value="10240" /> 7 <param name="MaxSizeRollBackups" value="100" /> 8 <param name="StaticLogFileName" value="false" /> 9 <param name="DatePattern" value="yyyyMMdd'.log'" /> 10 <param name="RollingStyle" value="Date" /> 11 <layout type="log4net.Layout.PatternLayout"> 12 <param name="ConversionPattern" value="%n日誌時間:%d [%t] %n日誌級別:%-5p %n日 志 類:%c [%L] %n%m %n" /> 13 </layout> 14 </appender> 15 <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> 16 <layout type="log4net.Layout.PatternLayout"> 17 <conversionPattern value="%5level [%thread] - %message%newline" /> 18 </layout> 19 </appender> 20 <root> 21 <level value="INFO" /> 22 <appender-ref ref="ConsoleAppender" /> 23 <appender-ref ref="FileAppender" /> 24 </root> 25 </log4net>
好了,接下來運行程序,就能夠看見任務已經啓動了:
安裝window服務
用了Toshelf後,不須要用InstallUtil.exe來安裝服務了,很簡單的一句,直接控制檯下執行:WindowControl.exe install 就行了。
每一次開發都是一次進步和積累,每一次分享都是一次學習和複習的機會,但願你們有更好的東西也能分享出來,一塊兒學習。