一,新建控制台应用程序 

二,选中项目,右键 — 管理 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 

 

posted on 2021-09-14 10:47  £冷☆月№  阅读(281)  评论(0编辑  收藏  举报