正如標題所示,文章主要是圍繞Quartz.Net做業調度框架話題展開的,內容出自博主學習官方Examples的學習心得與體會,文中不免會有錯誤之處,還請指出得以指教。web
一: Calendarapp
前面演示過根據秒-分-時 日-月-星期等觸發時機來定義一個輪詢的做業調度,在實際生活中,除此以外還有根據日從來調度做業,根據日曆定義觸發輪詢週期也是比較經常使用常見的功能。
框架
在Quartz.Net中日曆是由AnnualCalendar類定義的,實例化一個AnnualCalendar對象,往這個對象添加自定義的日期構成自定義的一個日曆觸發時機集合。tcp
舉個例子:ide
//日曆對象 AnnualCalendar holidays = new AnnualCalendar(); // 元旦 DateTime NewYearDay = new DateTime(DateTime.UtcNow.Year, 1, 1); holidays.SetDayExcluded(NewYearDay , true); // 國慶節 DateTime NationalDay= new DateTime(DateTime.UtcNow.Year, 10, 1); holidays.SetDayExcluded(NationalDay, true); // 光棍節 DateTime SinglesDay= new DateTime(DateTime.UtcNow.Year, 11, 11); holidays.SetDayExcluded(SinglesDay, true);
有了日曆對象以後,須要將日曆對象附加到調度實例上,而且在觸發器中使用ModifiedByCalendar("日曆對象")來指定按照日曆對象進行調度。學習
下面貼出根據日曆對象指定的日期進行做業調度的代碼(僅供參考):ui
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using Quartz.Impl.Calendar; using Quartz.Impl; using Common.Logging; namespace Quartz.Examples { /// <summary> /// This example will demonstrate how calendars can be used /// to exclude periods of time when scheduling should not /// take place. /// 一個根據日曆(節假日)來設定調度做業的演示 /// </summary> /// <author>Marko Lahma (.NET)</author> public class CalendarExample : IExample { public string Name { get { return GetType().Name; } } public virtual void Run() { ILog log = LogManager.GetLogger(typeof(CalendarExample)); log.Info("------- Initializing ----------------------"); // First we must get a reference to a scheduler ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); log.Info("------- Initialization Complete -----------"); log.Info("------- Scheduling Jobs -------------------"); // Add the holiday calendar to the schedule //就是自定義節假日 好比咱們能夠事先根據日曆設定7月4號,10月31號,12月25號做爲調度時機 AnnualCalendar holidays = new AnnualCalendar(); // fourth of July (July 4) DateTime fourthOfJuly = new DateTime(DateTime.UtcNow.Year, 7, 4); holidays.SetDayExcluded(fourthOfJuly, true); // halloween (Oct 31) DateTime halloween = new DateTime(DateTime.UtcNow.Year, 10, 31);//10月31號 holidays.SetDayExcluded(halloween, true); // christmas (Dec 25) DateTime christmas = new DateTime(DateTime.UtcNow.Year, 12, 25); holidays.SetDayExcluded(christmas, true); // tell the schedule about our holiday calendar sched.AddCalendar("holidays", holidays, false, false); //設定開啓調度日曆的時間 DateTimeOffset runDate = DateBuilder.DateOf(10, 0, 0, 31, 10);//10.31早上10點開啓調度做業 IJobDetail job = JobBuilder.Create<SimpleJob>() .WithIdentity("job1", "group1") .Build(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartAt(runDate) .WithSimpleSchedule(x => x.WithIntervalInHours(1).RepeatForever()) .ModifiedByCalendar("holidays") .Build(); // schedule the job and print the first run date DateTimeOffset firstRunTime = sched.ScheduleJob(job, trigger); log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, firstRunTime.ToString("r"), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds)); log.Info("------- Starting Scheduler ----------------"); sched.Start(); log.Info("------- Waiting 30 seconds... --------------"); try { // wait 30 seconds to show jobs Thread.Sleep(30 * 1000); // executing... } catch (ThreadInterruptedException) { } // shut down the scheduler log.Info("------- Shutting Down ---------------------"); sched.Shutdown(true); log.Info("------- Shutdown Complete -----------------"); SchedulerMetaData metaData = sched.GetMetaData(); log.Info(string.Format("Executed {0} jobs.", metaData.NumberOfJobsExecuted)); } } }
二:監聽對象this
在Quartz.Net框架中提供了一個監聽器IJobListener接口,實現該接口實例化一個子類,這個子類能夠監聽一個做業id來觸發自己的void JobWasExecuted(IJobExecutionContext inContext, JobExecutionException inException)方法:這個方法裏面帶了IJobExecutionContext inContext參數,inContext.Scheduler其實就是程序的調度實例,咱們知道經過調度實例能夠添加做業以及觸發器(定製一個輪詢的調度任務)而且Start()開啓執行任務。spa
這樣看來,經過實現IJobListener接口獲得監聽器類中的JobWasExecuted()方法裏能夠再次定義輪詢調度做業。.net
好比當須要知足如下需求時可使用監聽器來實現:
=>在A任務順利開啓執行後,輪詢調度B任務。(此時B任務就定義在監聽器類裏面)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using Quartz.Impl.Matchers; using Quartz.Impl; using Common.Logging; namespace Quartz.Examples { public class ListenerExample : IExample { public string Name { get { return GetType().Name; } } public virtual void Run() { ILog log = LogManager.GetLogger(typeof(ListenerExample)); log.Info("------- Initializing ----------------------"); // First we must get a reference to a scheduler ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); log.Info("------- Initialization Complete -----------"); log.Info("------- Scheduling Jobs -------------------"); // schedule a job to run immediately IJobDetail job = JobBuilder.Create<SimpleJob1>() .WithIdentity("job1") .Build(); ITrigger trigger = TriggerBuilder.Create() .WithIdentity("trigger1") .StartNow() .Build(); //IJobListener監聽器接口,實現該接口獲得自定義的Job1Listener類,在該類中實現JobWasExecuted()方法,在方法中能夠添加做業觸發器 //監聽類的意義更在於在一個做業成功運行以後,觸發綁定的另一些操做,這些操做在監聽類中定義並調度。 // Set up the listener //設定監聽程序,實例話Job1Listener()監聽類 IJobListener listener = new Job1Listener(); IMatcher<JobKey> matcher = KeyMatcher<JobKey>.KeyEquals(job.Key); sched.ListenerManager.AddJobListener(listener, matcher);//根據做業key爲響應做業添加監聽 sched.ScheduleJob(job, trigger); log.Info("------- Starting Scheduler ----------------"); sched.Start(); log.Info("------- Waiting 30 seconds... --------------"); try { // wait 30 seconds to show jobs Thread.Sleep(TimeSpan.FromSeconds(30)); // executing... } catch (ThreadInterruptedException) { } // shut down the scheduler log.Info("------- Shutting Down ---------------------"); sched.Shutdown(true); log.Info("------- Shutdown Complete -----------------"); SchedulerMetaData metaData = sched.GetMetaData(); log.Info(string.Format("Executed {0} jobs.", metaData.NumberOfJobsExecuted)); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Common.Logging; namespace Quartz.Examples { ///監聽程序能夠附加到做業中,附加了監聽器的做業在進行調度的時候,準備執行,否決,執行完畢三個狀態 ///而且在監聽程序能夠實例化IJobDetail類建立新做業 public class Job1Listener : IJobListener { private static readonly ILog log = LogManager.GetLogger(typeof(Job1Listener)); public virtual string Name { get { return "job1_to_job2"; } } public virtual void JobToBeExecuted(IJobExecutionContext inContext) { log.Info("Job1Listener says: Job Is about to be 執行.");//執行 } public virtual void JobExecutionVetoed(IJobExecutionContext inContext) { log.Info("Job1Listener says: Job Execution was 否決.");//否決 } public virtual void JobWasExecuted(IJobExecutionContext inContext, JobExecutionException inException) { log.Info("Job1Listener says: Job was 執行 完畢."); // Simple job #2 //監聽程序調度做業 IJobDetail job2 = JobBuilder.Create<SimpleJob2>() .WithIdentity("job2") .Build(); ITrigger trigger = TriggerBuilder.Create() .WithIdentity("job2Trigger") .StartNow() .Build(); try { // schedule the job to run! inContext.Scheduler.ScheduleJob(job2, trigger); } catch (SchedulerException e) { log.Warn("Unable to schedule job2!"); Console.Error.WriteLine(e.StackTrace); } } } }
三:插件配置
官方給出的名字叫作插件,其實我認爲,這只是一種關於如何調度做業的配置。
有三種配置方式:
1.經過代碼實例化NameValueCollection對象,往NameValueCollection對象以鍵值對形式賦值,而後在調度工廠對象中傳入該NameValueCollection對象獲得調度實例。
var properties = new NameValueCollection(); properties["quartz.plugin.triggHistory.type"] = "Quartz.Plugin.History.LoggingJobHistoryPlugin"; properties["quartz.plugin.jobInitializer.type"] = "Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin";//插件 properties["quartz.plugin.jobInitializer.fileNames"] = "quartz_jobs.xml";//讀取這個配置文件 properties["quartz.plugin.jobInitializer.failOnFileNotFound"] = "true"; properties["quartz.plugin.jobInitializer.scanInterval"] = "120";//120秒一次 // First we must get a reference to a scheduler ISchedulerFactory sf = new StdSchedulerFactory(properties); IScheduler sched = sf.GetScheduler();
sched.Start();
經過這樣方式無須再使用代碼定義做業對象IJobDetail以及觸發器ITrigger等,而是經過properties["quartz.plugin.jobInitializer.fileNames"]指定的xml文件來設置:
<?xml version="1.0" encoding="UTF-8"?> <job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"> <processing-directives> <overwrite-existing-data>true</overwrite-existing-data> </processing-directives> <schedule> <job> <name>jobName1</name> <group>jobGroup1</group> <description>jobDesciption1</description> <job-type>Quartz.Examples.SimpleJob3, Quartz.Examples</job-type> <durable>true</durable> <recover>false</recover> <job-data-map> <entry> <key>key0</key> <value>value0</value> </entry> <entry> <key>key1</key> <value>value1</value> </entry> <entry> <key>key2</key> <value>value2</value> </entry> </job-data-map> </job> <trigger> <simple> <name>simpleName</name> <group>simpleGroup</group> <description>SimpleTriggerDescription</description> <job-name>jobName1</job-name> <job-group>jobGroup1</job-group> <start-time>1982-06-28T18:15:00.0Z</start-time> <end-time>2020-05-04T18:13:51.0Z</end-time> <misfire-instruction>SmartPolicy</misfire-instruction> <repeat-count>100</repeat-count> <repeat-interval>3000</repeat-interval> </simple> </trigger> </schedule> </job-scheduling-data>
注意點:
1.1 quartz_jobs.xml必須設置爲始終複製=>右鍵屬性,複製到輸出目錄選項選擇始終複製
1.2 要將NameValueCollection對象傳入StdSchedulerFactory工廠中以獲得調試實例對象
ISchedulerFactory sf = new StdSchedulerFactory(properties);
2.經過app.config或者web.config配置文件
經過這種方式定義調度做業的信息將會放置在app.config或web.config配置文件中,在代碼中只須要獲得一個無參的StdSchedulerFactory()實例對象,開啓調度便可:
ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); // start the schedule sched.Start();
app.config配置文件內容:
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> <sectionGroup name="common"> <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" /> </sectionGroup> </configSections> <common> <logging> <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4net1213"> <arg key="configType" value="INLINE" /> </factoryAdapter> </logging> </common> <log4net> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%d [%t] %-5p %l - %m%n" /> </layout> </appender> <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%d [%t] %-5p %l - %m%n" /> </layout> </appender> <root> <level value="INFO" /> <appender-ref ref="ConsoleAppender" /> <!-- uncomment to enable event log appending --> <!-- <appender-ref ref="EventLogAppender" /> --> </root> </log4net> <!-- We use quartz.config for this server, you can always use configuration section if you want to. Configuration section has precedence here. --> <quartz> <add key="quartz.plugin.triggHistory.type" value="Quartz.Plugin.History.LoggingJobHistoryPlugin"/> <add key="quartz.plugin.jobInitializer.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin"/> <add key="quartz.plugin.jobInitializer.fileNames" value="quartz_jobs.xml"/> <add key="quartz.plugin.jobInitializer.failOnFileNotFound" value="true"/> <add key="quartz.plugin.jobInitializer.scanInterval" value="120"/> </quartz> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="Common.Logging" publicKeyToken="af08829b84f0328e" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-1.2.13.0" newVersion="1.2.13.0" /> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
在app.config中會指定調度任務信息的一個路徑,好比quartz_jobs.xml文件,經過讀取這個xml文件來獲取調度任務。
2.1 quartz_jobs.xml必須設置爲始終複製=>右鍵屬性,複製到輸出目錄選項選擇始終複製
3.經過quartz.config配置文件
# You can configure your scheduler in either <quartz> configuration section # or in quartz properties file # Configuration section has precedence quartz.scheduler.instanceName = ServerScheduler # configure thread pool info quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz quartz.threadPool.threadCount = 10 quartz.threadPool.threadPriority = Normal #--------------------------------*************plugin配置------------------------------------ # job initialization plugin handles our xml reading, without it defaults are used quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz quartz.plugin.xml.fileNames = ~/quartz_jobs.xml # export this server to remoting context quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz quartz.scheduler.exporter.port = 555 quartz.scheduler.exporter.bindName = QuartzScheduler quartz.scheduler.exporter.channelType = tcp quartz.scheduler.exporter.channelName = httpQuartz
在代碼中只須要獲得一個無參的StdSchedulerFactory()實例對象,開啓調度便可:
ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); // start the schedule sched.Start();
3.1 quartz_jobs.xml必須設置爲始終複製=>右鍵屬性,複製到輸出目錄選項選擇始終複製
3.2 quartz.config必須設置爲始終複製=>右鍵屬性,複製到輸出目錄選項選擇始終複製