想必大家在项目中处理简单的后台持续任务或者定时触发任务的时候均使用 Thread 或者 Task 来完成,但是项目中的这种需求一旦多了的话就得将任务调度引入进来了,那今天就简单的介绍一下 Quartz.NET 基于 Windows 服务宿主是怎样配置使用的。
Quartz.NET 是一个优秀的任务调度框架,移植于 Java 版的 Quartz 。
示例环境
- .Net 4.5.2
- Quartz 2.4.1
- Common.Logging 3.3.1
- log4net 2.0.5
- Common.Logging.Log4Net1213 3.3.1
源码地址:https://github.com/Wlitsoft/QuartzNETWinServiceSample
配置
1. quartz.config
这个配置文件需要放在服务运行根目录下,用于指定 quartz 的一些运行配置,比如调度名称、线程池实现组件、线程池大小、任务配置文件路径等。
1 # You can configure your scheduler in either <quartz> configuration section 2 # or in quartz properties file 3 # Configuration section has precedence 4 5 quartz.scheduler.instanceName = QuartzNETWinServiceScheduler 6 7 # configure thread pool info 8 quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz 9 quartz.threadPool.threadCount = 10 10 quartz.threadPool.threadPriority = Normal 11 12 # job initialization plugin handles our xml reading, without it defaults are used 13 quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz 14 quartz.plugin.xml.fileNames = ~/Conf/jobs.config
暂时需求需要修改的只有一处,看最后一行 quartz.plugin.xml.fileNames = ~/Conf/jobs.config 指定任务的配置文件路径。
2. 任务配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- This file contains job definitions in schema version 2.0 format --> 3 <job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"> 4 5 <processing-directives> 6 <overwrite-existing-data>true</overwrite-existing-data> 7 </processing-directives> 8 9 <schedule> 10 <job> 11 <name>Job1</name> 12 <group>Jobs</group> 13 <description>任务1</description> 14 <job-type>Wlitsoft.ProjectSample.QuartzNETWinService.Job.Job1,QuartzNETWinService</job-type> 15 <durable>true</durable> 16 <recover>false</recover> 17 </job> 18 <trigger> 19 <simple> 20 <name>Job1Trigger</name> 21 <group>Jobs</group> 22 <description>每 30 秒执行一次</description> 23 <job-name>Job1</job-name> 24 <job-group>Jobs</job-group> 25 <repeat-count>-1</repeat-count> 26 <repeat-interval>30000</repeat-interval> 27 </simple> 28 </trigger> 29 </schedule> 30 </job-scheduling-data>
以下为配置文件属性:
- 任务 (job 节点)
job 节点项说明 | ||||
名称 | 类型 | 是否必填 | 默认值 | 描述 |
name | string | Y | 任务名称 | |
group | string | N | 默认组名 | 任务组名称 |
description | string | N | 任务描述 | |
job-type | string | Y | 任务的类型全名称,一般实现 IJob 接口 | |
durable | boolean | N | false | 任务完成后是否依然保存到数据库 |
recover | boolean | N | false | 应用或服务重启之后是否忽略过期任务 |
- 触发器 (trigger 节点)
下面说下最常用的两种触发器:
1)简单触发器(simple 节点)用于触发定时轮训执行的任务。
simple 节点项说明 | ||||
名称 | 类型 | 是否必填 | 默认值 | 描述 |
name | string | Y | 触发器名称 | |
group | string | N | 默认组名 | 触发器名称 |
description | string | N | 触发器描述 | |
job-name | string | Y | 要触发的任务的名称 | |
job-group | string | Y | 要触发的任务的组名称 | |
repeat-count | int | Y | 0 | 重复次数(0:不执行;-1:不限次数) |
repeat-interval | long | Y | 0 | 间隔时间(单位:毫秒) |
start-time | date | N | 当前时间 | 开始时间 |
end-time | date | N | 结束时间(如果不指定则一直执行直到重复次数) |
2)Cron 触发器(cron 节点)根据 cron 表达式触发任务。
cron 节点项说明 | ||||
名称 | 类型 | 是否必填 | 默认值 | 描述 |
name | string | Y | 触发器名称 | |
group | string | N | 默认组名 | 触发器名称 |
description | string | N | 触发器描述 | |
job-name | string | Y | 要触发的任务的名称 | |
job-group | string | Y | 要触发的任务的组名称 | |
cron | string | Y | 规则表达式 | |
start-time | date | N | 当前时间 | 开始时间 |
end-time | date | N | 结束时间 |
注:cron 表达式在线生成:http://cron.qqe2.com
3. 日志配置文件
1) app.config
- configSctions
1 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> 2 <sectionGroup name="common"> 3 <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" /> 4 </sectionGroup>
- commong.logging 配置
1 <common> 2 <logging> 3 <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net1213"> 4 <arg key="configType" value="FILE-WATCH" /> 5 <arg key="configFile" value="~/Conf/log4net.config" /> 6 <arg key="level" value="INFO" /> 7 </factoryAdapter> 8 </logging> 9 </common>
- configType : 用于指定日子配置文件类型,取值:INLINE - 在当前配置文件总;FILE-WATCH - 配置文件中。
- configFile:配置文件路径。
- level:日子输出级别。
- log4net 配置
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <configSections> 4 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> 5 </configSections> 6 <log4net> 7 8 <logger name="job1" additivity="false" > 9 <level value="ALL" /> 10 <appender-ref ref="Job1TxtAppender" /> 11 </logger> 12 13 <appender name="Job1TxtAppender" type="log4net.Appender.RollingFileAppender"> 14 <file value="log/job1.txt" /> 15 <appendToFile value="true" /> 16 <param name="MaxSizeRollBackups" value="10"/> 17 <param name="MaximumFileSize" value="10240KB"/> 18 <param name="RollingStyle" value="Size"/> 19 <param name="StaticLogFileName" value="true"/> 20 <layout type="log4net.Layout.PatternLayout"> 21 <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" /> 22 </layout> 23 </appender> 24 25 <appender name="InfoFileAppender" type="log4net.Appender.RollingFileAppender"> 26 <file value="log/" /> 27 <appendToFile value="true" /> 28 <param name="DatePattern" value="yyyy-MM-dd'.txt'" /> 29 <rollingStyle value="Date" /> 30 <maxSizeRollBackups value="100" /> 31 <maximumFileSize value="1024KB" /> 32 <staticLogFileName value="false" /> 33 <Encoding value="UTF-8" /> 34 <filter type="log4net.Filter.LevelRangeFilter"> 35 <param name="LevelMin" value="INFO" /> 36 <param name="LevelMax" value="INFO" /> 37 </filter> 38 <layout type="log4net.Layout.PatternLayout"> 39 <conversionPattern value="%date %-5level %logger - %message%newline" /> 40 </layout> 41 </appender> 42 <appender name="ErrorFileAppender" type="log4net.Appender.RollingFileAppender"> 43 <file value="log/error.txt" /> 44 <appendToFile value="true" /> 45 <rollingStyle value="Size" /> 46 <maxSizeRollBackups value="100" /> 47 <maximumFileSize value="10240KB" /> 48 <staticLogFileName value="true" /> 49 <Encoding value="UTF-8" /> 50 <filter type="log4net.Filter.LevelRangeFilter"> 51 <param name="LevelMin" value="WARN" /> 52 <param name="LevelMax" value="FATAL" /> 53 </filter> 54 <layout type="log4net.Layout.PatternLayout"> 55 <conversionPattern value="%date %-5level %logger - %message%newline" /> 56 </layout> 57 </appender> 58 <root> 59 <level value="INFO" /> 60 <appender-ref ref="InfoFileAppender" /> 61 <appender-ref ref="ErrorFileAppender" /> 62 </root> 63 </log4net> 64 65 </configuration>
主程序代码:
1 using System.ServiceProcess; 2 using Common.Logging; 3 using Quartz; 4 using Quartz.Impl; 5 6 namespace Wlitsoft.ProjectSample.QuartzNETWinService 7 { 8 public partial class MainService : ServiceBase 9 { 10 #region 私有属性 11 12 //日志记录这。 13 private readonly ILog _logger; 14 15 //调度器。 16 private readonly IScheduler _scheduler; 17 18 #endregion 19 20 #region 构造方法 21 22 /// <summary> 23 /// 初始化 <see cref="MainService"/> 类的新实例。 24 /// </summary> 25 public MainService() 26 { 27 InitializeComponent(); 28 this._logger = LogManager.GetLogger(this.GetType()); 29 StdSchedulerFactory factory = new StdSchedulerFactory(); 30 this._scheduler = factory.GetScheduler(); 31 } 32 33 #endregion 34 35 protected override void OnStart(string[] args) 36 { 37 this._scheduler.Start(); 38 this._logger.Info("服务启动"); 39 } 40 41 protected override void OnStop() 42 { 43 if (!this._scheduler.IsShutdown) 44 this._scheduler.Shutdown(); 45 this._logger.Info("服务停止"); 46 } 47 48 protected override void OnPause() 49 { 50 this._scheduler.PauseAll(); 51 base.OnPause(); 52 } 53 54 protected override void OnContinue() 55 { 56 this._scheduler.ResumeAll(); 57 base.OnContinue(); 58 } 59 } 60 }
示例任务代码:
1 using Common.Logging; 2 using Quartz; 3 4 namespace Wlitsoft.ProjectSample.QuartzNETWinService.Job 5 { 6 public class Job1 : IJob 7 { 8 //日志构造者。 9 private static readonly ILog Logger = LogManager.GetLogger("job1"); 10 11 #region Implementation of IJob 12 13 /// <summary> 14 /// Called by the <see cref="T:Quartz.IScheduler" /> when a <see cref="T:Quartz.ITrigger" /> 15 /// fires that is associated with the <see cref="T:Quartz.IJob" />. 16 /// </summary> 17 /// <remarks> 18 /// The implementation may wish to set a result object on the 19 /// JobExecutionContext before this method exits. The result itself 20 /// is meaningless to Quartz, but may be informative to 21 /// <see cref="T:Quartz.IJobListener" />s or 22 /// <see cref="T:Quartz.ITriggerListener" />s that are watching the job's 23 /// execution. 24 /// </remarks> 25 /// <param name="context">The execution context.</param> 26 public void Execute(IJobExecutionContext context) 27 { 28 string jobDes = context.JobDetail.Description; 29 Logger.Info($"{jobDes}运行"); 30 } 31 32 #endregion 33 } 34 }
源码地址:https://github.com/Wlitsoft/QuartzNETWinServiceSample
推荐阅读:一个技术汪的开源梦
-------------------------------------------------------------
生命不息,挖坑不止!