最新项目中的windowService全部换成Quartz.NET和TopShelf,总结了一下配置的过程,写个比较详细的教程
1.下载和安装Quartz.NET和TopShelf
直接在NuGet里面敲Quartz.net和TopShelf 安装到自己项目就可以了 ,建议直接用NuGet,毕竟版本和插件管理是比较麻烦的事,Ps:TopShelf是将任何一个函数变成一个window 服务的工具,啥都可以变成服务
安装Quartz.NET
安装 TopShelf
首先是另一位大神的微博:http://www.cnblogs.com/jys509/p/4628926.html (微博很清新,内容很详细,当初我是看这里装的)
源码下载:Quartz 2.3 示例源码下载
Quartz.NET 官网地址:http://www.quartz-scheduler.net/
Quartz.NET 官方使用教程地址:http://www.quartz-scheduler.net/documentation/quartz-2.x/tutorial/using-quartz.html (看翻译好的不如自己边机翻边自己看,也比看翻译好的舒服,别人都嚼过了怎么会香)
Quartz.NET 官网安装教程地址:http://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html
2.配置QuartZ
初始配置
对于QuartZ初始配置的方式有三种:
1.app.config里面直接进行配置(推荐)
先在 <configSections>目录下加配置,如下内容,log4net和entityFramework 是我们项目自己用的,可以无视
1 <configSections> 2 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a" /> 3 <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> 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>
然后在<configuration>根目录下加入<quartz>目录就可以了,这种方式比较方便,也统一了config
1 <quartz> 2 <add key="quartz.plugin.xml.fileNames" value="~/quartz_jobs.xml"/> 3 <add key="quartz.plugin.xml.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz"/> 4 <add key="quartz.scheduler.instanceName" value=" EDMServiceQuartzTest"/> 5 <add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz"/> 6 <add key="quartz.threadPool.threadCount" value="10"/> 7 <add key="quartz.threadPool.threadPriority" value="Normal"/> 8 </quartz>
2.添加quartz.config文件或者在代码中配置文件
quartz.config:文件大概长这样,直接放到根目录就可以了,官网demo里面是没有的,所以不太建议用这种方法
# 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 # 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
记得要在属性-复制到输出目录,改成始终复制
代码中配置文件:这种是最不推荐的了,但是官网里面有demo,Example15,所以就放出来看看,需要动态配置的可以看下,代码如下(Example15)

1 using System.Collections.Specialized; 2 using System.Threading; 3 4 using Common.Logging; 5 6 using Quartz.Impl; 7 using Quartz.Impl.Calendar; 8 9 #region License 10 /* 11 * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. 12 * 13 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 14 * use this file except in compliance with the License. You may obtain a copy 15 * of the License at 16 * 17 * http://www.apache.org/licenses/LICENSE-2.0 18 * 19 * Unless required by applicable law or agreed to in writing, software 20 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 21 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 22 * License for the specific language governing permissions and limitations 23 * under the License. 24 * 25 */ 26 #endregion 27 28 using System.Collections.Specialized; 29 using System.Threading; 30 31 using Common.Logging; 32 33 using Quartz.Impl; 34 using Quartz.Impl.Calendar; 35 36 namespace Quartz.Examples.Example15 37 { 38 /// <summary> 39 /// This example will demonstrate how configuration can be 40 /// done using an XML file. 41 /// </summary> 42 /// <author>Marko Lahma (.NET)</author> 43 public class XmlConfigurationExample : IExample 44 { 45 public string Name 46 { 47 get { return GetType().Name; } 48 } 49 50 public void Run() 51 { 52 ILog log = LogManager.GetLogger(typeof(XmlConfigurationExample)); 53 54 log.Info("------- Initializing ----------------------"); 55 56 // First we must get a reference to a scheduler 57 NameValueCollection properties = new NameValueCollection(); 58 properties["quartz.scheduler.instanceName"] = "XmlConfiguredInstance"; 59 60 // set thread pool info 61 properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz"; 62 properties["quartz.threadPool.threadCount"] = "5"; 63 properties["quartz.threadPool.threadPriority"] = "Normal"; 64 65 // job initialization plugin handles our xml reading, without it defaults are used 66 properties["quartz.plugin.xml.type"] = "Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz"; 67 properties["quartz.plugin.xml.fileNames"] = "~/quartz_jobs.xml"; 68 69 70 ISchedulerFactory sf = new StdSchedulerFactory(properties); 71 IScheduler sched = sf.GetScheduler(); 72 73 // we need to add calendars manually, lets create a silly sample calendar 74 var dailyCalendar = new DailyCalendar("00:01", "23:59"); 75 dailyCalendar.InvertTimeRange = true; 76 sched.AddCalendar("cal1", dailyCalendar, false, false); 77 78 log.Info("------- Initialization Complete -----------"); 79 80 // all jobs and triggers are now in scheduler 81 82 83 // Start up the scheduler (nothing can actually run until the 84 // scheduler has been started) 85 sched.Start(); 86 log.Info("------- Started Scheduler -----------------"); 87 88 // wait long enough so that the scheduler as an opportunity to 89 // fire the triggers 90 log.Info("------- Waiting 30 seconds... -------------"); 91 92 try 93 { 94 Thread.Sleep(30*1000); 95 } 96 catch (ThreadInterruptedException) 97 { 98 } 99 100 // shut down the scheduler 101 log.Info("------- Shutting Down ---------------------"); 102 sched.Shutdown(true); 103 log.Info("------- Shutdown Complete -----------------"); 104 } 105 } 106 }
2.配置Trigger和Job
1.通过quartz_jobs.xml配置(推荐)
刚刚下载好的项目里面是没有这个文件的,可以直接新建一个或者直接把官方Demo里面的文件复制过来,大概长这样

1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 version="2.0"> 6 7 <processing-directives> 8 <overwrite-existing-data>true</overwrite-existing-data> 9 </processing-directives> 10 11 <schedule> 12 13 <job> 14 <name>jobName1</name> 15 <group>jobGroup1</group> 16 <description>jobDesciption1</description> 17 <job-type>Quartz.Examples.Example15.SimpleJob, Quartz.Examples</job-type> 18 <durable>true</durable> 19 <recover>false</recover> 20 <job-data-map> 21 <entry> 22 <key>key0</key> 23 <value>value0</value> 24 </entry> 25 <entry> 26 <key>key1</key> 27 <value>value1</value> 28 </entry> 29 <entry> 30 <key>key2</key> 31 <value>value2</value> 32 </entry> 33 </job-data-map> 34 </job> 35 36 <trigger> 37 <simple> 38 <name>simpleName</name> 39 <group>simpleGroup</group> 40 <description>SimpleTriggerDescription</description> 41 <job-name>jobName1</job-name> 42 <job-group>jobGroup1</job-group> 43 <start-time>1982-06-28T18:15:00.0Z</start-time> 44 <end-time>2020-05-04T18:13:51.0Z</end-time> 45 <misfire-instruction>SmartPolicy</misfire-instruction> 46 <repeat-count>100</repeat-count> 47 <repeat-interval>3000</repeat-interval> 48 </simple> 49 </trigger> 50 51 </schedule> 52 53 </job-scheduling-data>
首先要在1里面添加一个节点 quartz.plugin.xml.fileNames=~/quartz_jobs.xml,然后放入xml就可以了
节点的具体用途可以看看最上面另一个文章的链接,<trigger>节点的配置推荐<cron>
要注意的是配置<trigger><job> 的时候和每个节点要按照一定的顺序,
比如<name></name>必须在<job-name></job-name>前面 否则就会出错,具体的看官方demo里面的quartz_jobs.xml,试一试就知道了是否可以添加了(自己项目为啥出不来?笔者也在研究)
2.直接在代码里面配置
官网Demo里面很多都是在代码里面编写的,不太推荐,但是这种初始化的风格的确可以拿来学习一下。拿几个例子看下:
官网的HelloJob(Example1)

1 #region License 2 3 /* 4 * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 * use this file except in compliance with the License. You may obtain a copy 8 * of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 * License for the specific language governing permissions and limitations 16 * under the License. 17 * 18 */ 19 20 #endregion 21 22 using System; 23 using System.Threading; 24 25 using Common.Logging; 26 #if !NET_20 27 #endif 28 using Quartz.Impl; 29 30 namespace Quartz.Examples.Example1 31 { 32 /// <summary> 33 /// This Example will demonstrate how to start and shutdown the Quartz 34 /// scheduler and how to schedule a job to run in Quartz. 35 /// </summary> 36 /// <author>Bill Kratzer</author> 37 /// <author>Marko Lahma (.NET)</author> 38 public class SimpleExample : IExample 39 { 40 public string Name 41 { 42 get { throw new NotImplementedException(); } 43 } 44 45 public virtual void Run() 46 { 47 ILog log = LogManager.GetLogger(typeof (SimpleExample)); 48 49 log.Info("------- Initializing ----------------------"); 50 51 // First we must get a reference to a scheduler 52 ISchedulerFactory sf = new StdSchedulerFactory(); 53 IScheduler sched = sf.GetScheduler(); 54 55 log.Info("------- Initialization Complete -----------"); 56 57 58 // computer a time that is on the next round minute 59 DateTimeOffset runTime = DateBuilder.EvenMinuteDate(DateTimeOffset.UtcNow); 60 61 log.Info("------- Scheduling Job -------------------"); 62 63 // define the job and tie it to our HelloJob class 64 IJobDetail job = JobBuilder.Create<HelloJob>() 65 .WithIdentity("job1", "group1") 66 .Build(); 67 68 // Trigger the job to run on the next round minute 69 ITrigger trigger = TriggerBuilder.Create() 70 .WithIdentity("trigger1", "group1") 71 .StartAt(runTime) 72 .Build(); 73 74 // Tell quartz to schedule the job using our trigger 75 sched.ScheduleJob(job, trigger); 76 log.Info(string.Format("{0} will run at: {1}", job.Key, runTime.ToString("r"))); 77 78 // Start up the scheduler (nothing can actually run until the 79 // scheduler has been started) 80 sched.Start(); 81 log.Info("------- Started Scheduler -----------------"); 82 83 // wait long enough so that the scheduler as an opportunity to 84 // run the job! 85 log.Info("------- Waiting 65 seconds... -------------"); 86 87 // wait 65 seconds to show jobs 88 Thread.Sleep(TimeSpan.FromSeconds(65)); 89 90 // shut down the scheduler 91 log.Info("------- Shutting Down ---------------------"); 92 sched.Shutdown(true); 93 log.Info("------- Shutdown Complete -----------------"); 94 } 95 } 96 }
Example5:一个job怎么关联两个trigger

1 #region License 2 3 /* 4 * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 * use this file except in compliance with the License. You may obtain a copy 8 * of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 * License for the specific language governing permissions and limitations 16 * under the License. 17 * 18 */ 19 20 #endregion 21 22 using System; 23 using System.Threading; 24 25 using Common.Logging; 26 27 using Quartz.Impl; 28 29 namespace Quartz.Examples.Example5 30 { 31 /// <summary> 32 /// Demonstrates the behavior of <see cref="PersistJobDataAfterExecutionAttribute" />, 33 /// as well as how misfire instructions affect the firings of triggers of 34 /// that have <see cref="DisallowConcurrentExecutionAttribute" /> present - 35 /// when the jobs take longer to execute that the frequency of the trigger's 36 /// repetition. 37 /// </summary> 38 /// <remarks> 39 /// <para> 40 /// While the example is running, you should note that there are two triggers 41 /// with identical schedules, firing identical jobs. The triggers "want" to fire 42 /// every 3 seconds, but the jobs take 10 seconds to execute. Therefore, by the 43 /// time the jobs complete their execution, the triggers have already "misfired" 44 /// (unless the scheduler's "misfire threshold" has been set to more than 7 45 /// seconds). You should see that one of the jobs has its misfire instruction 46 /// set to <see cref="MisfireInstruction.SimpleTrigger.RescheduleNowWithExistingRepeatCount" />, 47 /// which causes it to fire immediately, when the misfire is detected. The other 48 /// trigger uses the default "smart policy" misfire instruction, which causes 49 /// the trigger to advance to its next fire time (skipping those that it has 50 /// missed) - so that it does not refire immediately, but rather at the next 51 /// scheduled time. 52 /// </para> 53 /// </remarks> 54 /// <author><a href="mailto:bonhamcm@thirdeyeconsulting.com">Chris Bonham</a></author> 55 /// <author>Marko Lahma (.NET)</author> 56 public class MisfireExample : IExample 57 { 58 public string Name 59 { 60 get { throw new NotImplementedException(); } 61 } 62 63 public virtual void Run() 64 { 65 ILog log = LogManager.GetLogger(typeof (MisfireExample)); 66 67 log.Info("------- Initializing -------------------"); 68 69 // First we must get a reference to a scheduler 70 ISchedulerFactory sf = new StdSchedulerFactory(); 71 IScheduler sched = sf.GetScheduler(); 72 73 log.Info("------- Initialization Complete -----------"); 74 75 log.Info("------- Scheduling Jobs -----------"); 76 77 // jobs can be scheduled before start() has been called 78 79 // get a "nice round" time a few seconds in the future... 80 81 DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 15); 82 83 // statefulJob1 will run every three seconds 84 // (but it will delay for ten seconds) 85 IJobDetail job = JobBuilder.Create<StatefulDumbJob>() 86 .WithIdentity("statefulJob1", "group1") 87 .UsingJobData(StatefulDumbJob.ExecutionDelay, 10000L) 88 .Build(); 89 90 ISimpleTrigger trigger = (ISimpleTrigger) TriggerBuilder.Create() 91 .WithIdentity("trigger1", "group1") 92 .StartAt(startTime) 93 .WithSimpleSchedule(x => x.WithIntervalInSeconds(3).RepeatForever()) 94 .Build(); 95 96 DateTimeOffset ft = sched.ScheduleJob(job, trigger); 97 log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, ft.ToString("r"), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds)); 98 99 // statefulJob2 will run every three seconds 100 // (but it will delay for ten seconds - and therefore purposely misfire after a few iterations) 101 job = JobBuilder.Create<StatefulDumbJob>() 102 .WithIdentity("statefulJob2", "group1") 103 .UsingJobData(StatefulDumbJob.ExecutionDelay, 10000L) 104 .Build(); 105 106 trigger = (ISimpleTrigger) TriggerBuilder.Create() 107 .WithIdentity("trigger2", "group1") 108 .StartAt(startTime) 109 .WithSimpleSchedule(x => x 110 .WithIntervalInSeconds(3) 111 .RepeatForever() 112 .WithMisfireHandlingInstructionNowWithExistingCount()) // set misfire instructions 113 .Build(); 114 ft = sched.ScheduleJob(job, trigger); 115 116 log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, ft.ToString("r"), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds)); 117 118 log.Info("------- Starting Scheduler ----------------"); 119 120 // jobs don't start firing until start() has been called... 121 sched.Start(); 122 123 log.Info("------- Started Scheduler -----------------"); 124 125 try 126 { 127 // sleep for ten minutes for triggers to file.... 128 Thread.Sleep(TimeSpan.FromMinutes(10)); 129 } 130 catch (ThreadInterruptedException) 131 { 132 } 133 134 log.Info("------- Shutting Down ---------------------"); 135 136 sched.Shutdown(true); 137 138 log.Info("------- Shutdown Complete -----------------"); 139 140 SchedulerMetaData metaData = sched.GetMetaData(); 141 log.Info(string.Format("Executed {0} jobs.", metaData.NumberOfJobsExecuted)); 142 } 143 } 144 }
最后,无论是哪种配置Trigger和Job的方法(xml或者代码)都要记住以下几点:
1.每个Job能对应多个Trigger,但是每个Trigger只能对应一个Job,所以建议把触发时间相同的作业放在一个Job里面也许会比较好,
2.Job配置到多个Trigger时需要配置 <durable>true</durable> 代码里面的是对应的是 .StoreDurably(),否则会报错
三.Job和Trigger入门
过几天会补上