Quartz与Topshelf结合实现window定时服务

  • A+
所属分类:.NET技术
摘要

一,新建控制台应用程序 二,选中项目,右键 — 管理 NuGet 程序包,添加四个:

一,新建控制台应用程序 

Quartz与Topshelf结合实现window定时服务

二,选中项目,右键 — 管理 NuGet 程序包,添加四个:

Quartz

Quartz.Plugins

Topshelf

log4net 

三,创建项目文件

三个配置文件:必须放在项目根目录下。

(1)log4net.config

Quartz与Topshelf结合实现window定时服务Quartz与Topshelf结合实现window定时服务

<?xml version="1.0" encoding="utf-8" ?> <configuration>   <configSections>     <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>   </configSections>    <log4net>     <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">       <!--日志路径-->       <param name= "File" value= "Logs/"/>       <!--是否是向文件中追加日志-->       <param name= "AppendToFile" value= "true"/>       <!--log保留天数-->       <param name= "MaxSizeRollBackups" value= "10"/>       <!--日志文件名是否是固定不变的-->       <param name= "StaticLogFileName" value= "false"/>       <!--日志文件名格式为:yyyy-MM-dd.log-->       <param name= "DatePattern" value= "yyyy-MM-dd&quot;.log&quot;"/>       <!--日志根据日期滚动-->       <param name= "RollingStyle" value= "Date"/>       <layout type="log4net.Layout.PatternLayout">         <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss,fff}] [%p] [%c] %m%n %n" />       </layout>     </appender>      <!-- 控制台前台显示日志 -->     <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">       <mapping>         <level value="ERROR" />         <foreColor value="Red, HighIntensity" />       </mapping>       <mapping>         <level value="Info" />         <foreColor value="Green" />       </mapping>       <layout type="log4net.Layout.PatternLayout">         <conversionPattern value="%n%date{HH:mm:ss,fff} [%-5level] %m" />       </layout>        <filter type="log4net.Filter.LevelRangeFilter">         <param name="LevelMin" value="Info" />         <param name="LevelMax" value="Fatal" />       </filter>     </appender>      <root>       <!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低) -->       <level value="all" />       <appender-ref ref="ColoredConsoleAppender"/>       <appender-ref ref="RollingLogFileAppender"/>     </root>   </log4net> </configuration>

View Code

(2)quartz.config 

Quartz与Topshelf结合实现window定时服务Quartz与Topshelf结合实现window定时服务

<?xml version="1.0" encoding="utf-8" ?> <!-- This file contains job definitions in schema version 2.0 format -->  <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>SampleJob</name>       <group>SampleGroup</group>       <description>Sample job for Quartz Server</description>       <job-type>QuartzTopshelf.Jobs.SampleJob, QuartzTopshelf</job-type>       <durable>true</durable>       <recover>false</recover>       <job-data-map>         <entry>           <key>key1</key>           <value>value1</value>         </entry>         <entry>           <key>key2</key>           <value>value2</value>         </entry>       </job-data-map>     </job>      <trigger>       <simple>         <name>SampleSimpleTrigger</name>         <group>SampleSimpleGroup</group>         <description>Simple trigger to simply fire sample job</description>         <job-name>SampleJob</job-name>         <job-group>SampleGroup</job-group>         <misfire-instruction>SmartPolicy</misfire-instruction>         <repeat-count>-1</repeat-count>         <repeat-interval>10000</repeat-interval>       </simple>     </trigger>     <trigger>       <cron>         <name>SampleCronTrigger</name>         <group>SampleCronGroup</group>         <description>Cron trigger to simply fire sample job</description>         <job-name>SampleJob</job-name>         <job-group>SampleGroup</job-group>         <misfire-instruction>SmartPolicy</misfire-instruction>         <cron-expression>0/10 * * * * ?</cron-expression>       </cron>     </trigger>     <trigger>       <calendar-interval>         <name>SampleCalendarIntervalTrigger</name>         <group>SampleCalendarIntervalGroup</group>         <description>Calendar interval trigger to simply fire sample job</description>         <job-name>SampleJob</job-name>         <job-group>SampleGroup</job-group>         <misfire-instruction>SmartPolicy</misfire-instruction>         <repeat-interval>15</repeat-interval>         <repeat-interval-unit>Second</repeat-interval-unit>       </calendar-interval>     </trigger>   </schedule> </job-scheduling-data>

View Code

也可以在项目配置文件App.config中配置,就不需要配置quartz.config 

App.config

Quartz与Topshelf结合实现window定时服务Quartz与Topshelf结合实现window定时服务

<?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" />   </configSections>    <quartz>        <add key="quartz.scheduler.instanceName" value="ServerScheduler" />        <add key="quartz.threadPool.type" value="Quartz.Simpl.DefaultThreadPool, Quartz" />     <add key="quartz.threadPool.maxConcurrency" value="10" />      <add key="quartz.plugin.xml.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz.Plugins" />     <add key="quartz.plugin.xml.fileNames" value="~/quartz_jobs.xml" />      <add key="quartz.scheduler.exporter.type" value="Quartz.Simpl.RemotingSchedulerExporter, Quartz" />     <add key="quartz.scheduler.exporter.port" value="555" />     <add key="quartz.scheduler.exporter.bindName" value="QuartzScheduler" />     <add key="quartz.scheduler.exporter.channelType" value="tcp" />     <add key="quartz.scheduler.exporter.channelName" value="httpQuartz" />   </quartz>       <startup>      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />   </startup>      <runtime>     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">       <dependentAssembly>         <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />         <bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" />       </dependentAssembly>     </assemblyBinding>   </runtime> </configuration>

View Code

(3)quartz_jobs.xml   具体我就不解释了,不懂的可以去查下Quartz.NET

Quartz与Topshelf结合实现window定时服务Quartz与Topshelf结合实现window定时服务

<?xml version="1.0" encoding="utf-8" ?> <!-- This file contains job definitions in schema version 2.0 format -->  <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>SampleJob</name>       <group>SampleGroup</group>       <description>Sample job for Quartz Server</description>       <job-type>Quartz.Server.Jobs.SampleJob, Quartz.Server</job-type>       <durable>true</durable>       <recover>false</recover>       <job-data-map>         <entry>           <key>key1</key>           <value>value1</value>         </entry>         <entry>           <key>key2</key>           <value>value2</value>         </entry>       </job-data-map>     </job>      <trigger>       <simple>         <name>SampleSimpleTrigger</name>         <group>SampleSimpleGroup</group>         <description>Simple trigger to simply fire sample job</description>         <job-name>SampleJob</job-name>         <job-group>SampleGroup</job-group>         <misfire-instruction>SmartPolicy</misfire-instruction>         <repeat-count>-1</repeat-count>         <repeat-interval>10000</repeat-interval>       </simple>     </trigger>     <trigger>       <cron>         <name>SampleCronTrigger</name>         <group>SampleCronGroup</group>         <description>Cron trigger to simply fire sample job</description>         <job-name>SampleJob</job-name>         <job-group>SampleGroup</job-group>         <misfire-instruction>SmartPolicy</misfire-instruction>         <cron-expression>0/10 * * * * ?</cron-expression>       </cron>     </trigger>     <trigger>       <calendar-interval>         <name>SampleCalendarIntervalTrigger</name>         <group>SampleCalendarIntervalGroup</group>         <description>Calendar interval trigger to simply fire sample job</description>         <job-name>SampleJob</job-name>         <job-group>SampleGroup</job-group>         <misfire-instruction>SmartPolicy</misfire-instruction>         <repeat-interval>15</repeat-interval>         <repeat-interval-unit>Second</repeat-interval-unit>       </calendar-interval>     </trigger>    </schedule> </job-scheduling-data>

View Code

三个类文件:

(1)Configuration.cs  配置类

Quartz与Topshelf结合实现window定时服务Quartz与Topshelf结合实现window定时服务

using System.Collections.Specialized; using System.Configuration;  namespace QuartzTopshelf {     /// <summary>     /// 定时服务的配置     /// </summary>     public class Configuration     {         private const string PrefixServerConfiguration = "quartz.server";         private const string KeyServiceName = PrefixServerConfiguration + ".serviceName";         private const string KeyServiceDisplayName = PrefixServerConfiguration + ".serviceDisplayName";         private const string KeyServiceDescription = PrefixServerConfiguration + ".serviceDescription";         private const string KeyServerImplementationType = PrefixServerConfiguration + ".type";          private const string DefaultServiceName = "QuartzServer";         private const string DefaultServiceDisplayName = "Quartz Server";         private const string DefaultServiceDescription = "Quartz Job Scheduling Server";         private static readonly string DefaultServerImplementationType = typeof(QuartzServer).AssemblyQualifiedName;          private static readonly NameValueCollection configuration;          /// <summary>         /// 初始化 Configuration 类         /// </summary>         static Configuration()         {             configuration = (NameValueCollection)ConfigurationManager.GetSection("quartz");         }          /// <summary>         /// 获取服务的名称         /// </summary>         /// <value>服务的名称</value>         public static string ServiceName         {             get { return GetConfigurationOrDefault(KeyServiceName, DefaultServiceName); }         }          /// <summary>         /// 获取服务的显示名称.         /// </summary>         /// <value>服务的显示名称</value>         public static string ServiceDisplayName         {             get { return GetConfigurationOrDefault(KeyServiceDisplayName, DefaultServiceDisplayName); }         }          /// <summary>         /// 获取服务描述         /// </summary>         /// <value>服务描述</value>         public static string ServiceDescription         {             get { return GetConfigurationOrDefault(KeyServiceDescription, DefaultServiceDescription); }         }          /// <summary>         /// 获取服务器实现的类型名称         /// </summary>         /// <value>服务器实现的类型</value>         public static string ServerImplementationType         {             get { return GetConfigurationOrDefault(KeyServerImplementationType, DefaultServerImplementationType); }         }          /// <summary>         /// 返回具有给定键的配置值。如果的配置不存在,则返回默认值。         /// </summary>         /// <param name="configurationKey">用于读取配置的键</param>         /// <param name="defaultValue">未找到配置时返回的默认值</param>         /// <returns>配置值</returns>         private static string GetConfigurationOrDefault(string configurationKey, string defaultValue)         {             string retValue = null;             if (configuration != null)             {                 retValue = configuration[configurationKey];             }              if (retValue == null || retValue.Trim().Length == 0)             {                 retValue = defaultValue;             }             return retValue;         }     } }

View Code

(2)QuartzServer.cs  定时服务类

Quartz与Topshelf结合实现window定时服务Quartz与Topshelf结合实现window定时服务

using System; using System.Threading.Tasks; using log4net; using Quartz; using Quartz.Impl; using Topshelf;  namespace QuartzTopshelf {     /// <summary>     /// Service interface for core Quartz.NET server.     /// </summary>     public interface IQuartzServer     {         /// <summary>         /// Initializes the instance of <see cref="IQuartzServer"/>.         /// Initialization will only be called once in server's lifetime.         /// </summary>         Task Initialize();          /// <summary>         /// Starts this instance.         /// </summary>         void Start();          /// <summary>         /// Stops this instance.         /// </summary>         void Stop();          /// <summary>         /// Pauses all activity in scheduler.         /// </summary>         void Pause();          /// <summary>         /// Resumes all activity in server.         /// </summary>         void Resume();     }      /// <summary>     /// The main server logic.     /// </summary>     public class QuartzServer : ServiceControl, IQuartzServer     {         private readonly ILog logger;         private ISchedulerFactory schedulerFactory;         private IScheduler scheduler;          /// <summary>         /// Initializes a new instance of the <see cref="QuartzServer"/> class.         /// </summary>         public QuartzServer()         {             logger = LogManager.GetLogger(GetType());         }          /// <summary>         /// Initializes the instance of the <see cref="QuartzServer"/> class.         /// </summary>         public virtual async Task Initialize()         {             try             {                 schedulerFactory = CreateSchedulerFactory();                 scheduler = await GetScheduler().ConfigureAwait(false);             }             catch (Exception e)             {                 logger.Error("Server initialization failed:" + e.Message, e);             }         }          /// <summary>         /// Gets the scheduler with which this server should operate with.         /// </summary>         /// <returns></returns>         protected virtual Task<IScheduler> GetScheduler()         {             return schedulerFactory.GetScheduler();         }          /// <summary>         /// Returns the current scheduler instance (usually created in <see cref="Initialize" />         /// using the <see cref="GetScheduler" /> method).         /// </summary>         protected virtual IScheduler Scheduler => scheduler;          /// <summary>         /// Creates the scheduler factory that will be the factory         /// for all schedulers on this instance.         /// </summary>         /// <returns></returns>         protected virtual ISchedulerFactory CreateSchedulerFactory()         {             return new StdSchedulerFactory();         }          /// <summary>         /// Starts this instance, delegates to scheduler.         /// </summary>         public virtual void Start()         {             try             {                 scheduler.Start();             }             catch (Exception ex)             {                 logger.Fatal(string.Format("Scheduler start failed: {0}", ex.Message), ex);                 throw;             }             //logger.Info("Scheduler started successfully");         }          /// <summary>         /// Stops this instance, delegates to scheduler.         /// </summary>         public virtual void Stop()         {             try             {                 scheduler.Shutdown(true);             }             catch (Exception ex)             {                 logger.Error(string.Format("Scheduler stop failed: {0}", ex.Message), ex);                 throw;             }              //logger.Info("Scheduler shutdown complete");         }          /// <summary>         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.         /// </summary>         public virtual void Dispose()         {             // no-op for now         }          /// <summary>         /// Pauses all activity in scheduler.         /// </summary>         public virtual void Pause()         {             scheduler.PauseAll();         }          /// <summary>         /// Resumes all activity in server.         /// </summary>         public void Resume()         {             scheduler.ResumeAll();         }          /// <summary>         /// TopShelf's method delegated to <see cref="Start()"/>.         /// </summary>         public bool Start(HostControl hostControl)         {             Start();             return true;         }          /// <summary>         /// TopShelf's method delegated to <see cref="Stop()"/>.         /// </summary>         public bool Stop(HostControl hostControl)         {             Stop();             return true;         }          /// <summary>         /// TopShelf's method delegated to <see cref="Pause()"/>.         /// </summary>         public bool Pause(HostControl hostControl)         {             Pause();             return true;         }          /// <summary>         /// TopShelf's method delegated to <see cref="Resume()"/>.         /// </summary>         public bool Continue(HostControl hostControl)         {             Resume();             return true;         }     } }

View Code

(3)QuartzServerFactory.cs  工厂类

Quartz与Topshelf结合实现window定时服务Quartz与Topshelf结合实现window定时服务

using System; using System.Reflection; using log4net;  namespace QuartzTopshelf {     /// <summary>     /// 用于创建Quartz服务实现的工厂类.     /// </summary>     public class QuartzServerFactory     {         private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);          /// <summary>         /// 创建Quartz.NET服务核心的新实例         /// </summary>         /// <returns></returns>         public static QuartzServer CreateServer()         {             string typeName = Configuration.ServerImplementationType;              Type t = Type.GetType(typeName, true);              //logger.Debug("正在创建服务器类型的新实例'" + typeName + "'");             QuartzServer retValue = (QuartzServer)Activator.CreateInstance(t);             //logger.Debug("已成功创建实例");             return retValue;         }     } }

View Code

服务启动类:Program.cs

Quartz与Topshelf结合实现window定时服务Quartz与Topshelf结合实现window定时服务

using System.IO; using System.Reflection; using Topshelf;  namespace QuartzTopshelf {     public static class Program     {         static void Main(string[] args)         {             // 从服务帐户的目录更改为更符合逻辑的目录             Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);              var logRepository = log4net.LogManager.GetRepository(Assembly.GetEntryAssembly());             log4net.Config.XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));              HostFactory.Run(x => {                 x.RunAsLocalSystem();                  x.SetDescription(Configuration.ServiceDescription);                 x.SetDisplayName(Configuration.ServiceDisplayName);                 x.SetServiceName(Configuration.ServiceName);                  x.Service(factory =>                 {                     QuartzServer server = QuartzServerFactory.CreateServer();                     server.Initialize().GetAwaiter().GetResult();                     return server;                 });             });         }     } }

View Code

四,添加Job类,以及在quartz_jobs.xml 配置job作业触发规则

SampleJob.cs

Quartz与Topshelf结合实现window定时服务Quartz与Topshelf结合实现window定时服务

using System; using System.Collections; using System.Reflection; using System.Threading.Tasks; using log4net; using Quartz;  namespace QuartzTopshelf.Jobs {     /// <summary>     /// A sample job that just prints info on console for demostration purposes.     /// </summary>     public sealed class SampleJob : IJob     {         private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);          /// <summary>         /// Called by the <see cref="IScheduler" /> when a <see cref="ITrigger" />         /// fires that is associated with the <see cref="IJob" />.         /// </summary>         /// <remarks>         /// The implementation may wish to set a  result object on the          /// JobExecutionContext before this method exits.  The result itself         /// is meaningless to Quartz, but may be informative to          /// <see cref="IJobListener" />s or          /// <see cref="ITriggerListener" />s that are watching the job's          /// execution.         /// </remarks>         /// <param name="context">The execution context.</param>         public async Task Execute(IJobExecutionContext context)         {             //通过配置文件传递参数             JobDataMap dataMap = context.JobDetail.JobDataMap;             string key1 = dataMap.GetString("key1");             logger.Info("key1 : " + key1);             string key2 = dataMap.GetString("key2");             logger.Info("key2 : " + key2);              logger.Info("SampleJob running...");             //Thread.Sleep(TimeSpan.FromSeconds(5));             await Console.Out.WriteLineAsync("SampleJob is executing.");             logger.Info("SampleJob run finished.");         }     } }

View Code

作业触发规则在上面quartz_jobs.xml文件中配置 

这里分析下特别提到下,job的trigger有以下几种常用的方法:

  • SimpleTrigger:简单的触发器(重点)

  • CalendarIntervalTrigger:日历触发器(可自行研究)

  • CronTrigger:Cron表达式触发器 (重点)

Quartz与Topshelf结合实现window定时服务

 

 项目结构图:

Quartz与Topshelf结合实现window定时服务

另外可以通过配置文件设置参数传递给作业:

Quartz与Topshelf结合实现window定时服务

 

 Quartz与Topshelf结合实现window定时服务

相关源代码地址:https://gitee.com/wyft/QuartzTopshelf