zoukankan      html  css  js  c++  java
  • Quartz与Topshelf结合实现window定时服务

    一,新建控制台应用程序 

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

    Quartz

    Quartz.Plugins

    Topshelf

    log4net 

    三,创建项目文件

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

    (1)log4net.config

    <?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 

    <?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

    <?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

    <?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  配置类

    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  定时服务类

    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  工厂类

    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

    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

    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表达式触发器 (重点)

     项目结构图:

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

     


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

  • 相关阅读:
    现在连Linux都搞不懂,当初我要是这么学习操作系统就好了!
    一时技痒,撸了个动态线程池,源码放Github了
    Java线程池ThreadPoolExecutor使用和分析(一)
    canch----1.对缓存的思考
    1.java 内存数据库--H2数据库使用指南
    What’s the difference between persist, save, merge and update? Which one should you use?
    primary key's generator in JPA entity
    STM32F103驱动M24256 256k存储芯片进行读写
    【Proteus+51单片机学习笔记】-51/52系列单片机简介
    【STM32项目笔记】STM32CubeMX+Keil+Proteus联合实现LED闪烁
  • 原文地址:https://www.cnblogs.com/wsk198726/p/15266528.html
Copyright © 2011-2022 走看看