您现在的位置是:首页 >技术交流 >使用Quartz.net + Topshelf完成服务调用网站首页技术交流

使用Quartz.net + Topshelf完成服务调用

凌霄玉阶非所愿 2023-07-01 04:00:03
简介使用Quartz.net + Topshelf完成服务调用

概述:

Quartz.NET 是一个开源作业调度库,可用于在 .NET 应用程序中调度和管理作业。它提供了一个灵活而强大的框架,用于调度作业在特定的日期和时间或以固定的时间间隔运行,并且还支持复杂的调度场景,例如 cron 表达式。

Quartz.NET 基于Quartz 库,该库最初是用 Java 开发的。Quartz.NET 的创建是为了将Quartz的强大功能带入 .NET 平台,它已成为 .NET 应用程序中调度作业的流行选择。

Quartz.NET 的一些关键特性包括:

  • 支持根据特定日期和时间或定期安排作业
  • 支持使用cron 表达式的复杂调度场景
  • 能够安排作业在分布式环境中的特定节点上运行
  • 支持集群、故障转移和负载平衡
  • 能够将作业和触发数据保存到数据库
  • 与流行的 .NET 框架集成,例如 ASP.NET 和 Spring.NET
    以下是配置及使用过程:
官方学习文档:
http://www.quartz-scheduler.net/documentation/index.html
使用实例介绍:
https://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html#nuget-package
官方源码下载:
https://sourceforge.net/projects/quartznet/files/quartznet/

1、根据官方源码下载地址我们找到一个文件版本进行下载
在这里插入图片描述

文件介绍:

在这里插入图片描述

文件介绍
Configuration.cs这个文件定义了Quartz.NET的配置类,包括调度器的实例化、作业存储、线程池、触发器监听器、作业监听器等配置项
IQuartzServer.cs这个文件定义了Quartz.NET的服务器接口,包括启动、停止、暂停和恢复调度器等方法
Program.cs入口程序,演示了如何使用Quartz.NET进行作业调度
quartz.config这个文件是Quartz.NET的配置文件,用于配置调度器的行为和特性,例如调度器类型、线程池大小、作业存储类型等等
quartz_jobs.xml这个文件定义了Quartz.NET的作业和触发器,包括作业名称、作业类型、触发器类型、触发器表达式等等
QuartzServer.cs这个文件实现了IQuartzServer接口,是Quartz.NET服务器的实现代码,包括调度器的启动、停止、暂停和恢复等方法
SampleJob.cs这个文件是一个示例作业,演示了如何实现一个简单的Quartz.NET作业
QuartzServerFactory.cs这个文件是Quartz.NET服务器的工厂类,用于创建和销毁Quartz.NET服务器实例

总的来说,这些文件是Quartz.NET框架的核心文件,包含了Quartz.NET的配置、调度器、作业和触发器等组件的实现。

接下来我们运行文件,可以看到quartz_job.xml文件中配置的是10秒运行一次,运行起来之后,每隔十秒会有输出
在这里插入图片描述

搭建一个Quartz,本人本地是基于.NET Framework 4.0搭建的(可使用更高版本)

1、安装下载NuGet程序包

Common.Logging 3.4.1.0
Common.Logging.Core 3.4.1.0
log4net 2.0.15.0
Quartz 2.6.2.0
Topshelf 3.3.152.0
Topshelf.Log4Net 3.3.152.0

  
Quartz:这是 Quartz.NET 的核心包,包含了调度器、作业、触发器等组件。
Common.Logging:这个包是一个通用的日志框架,Quartz.NET 使用它来记录日志。
Common.Logging.Log4Net:这个包是 Common.Logging 的 Log4Net 适配器,Quartz.NET 需要引用它来使用 Log4Net 记录日志
Topshelf:这是 Topshelf 的核心包,包含了将应用程序转换为 Windows 服务的功能。
Topshelf.Log4Net:这个包提供了 Log4Net 日志框架的支持,允许您在 Topshelf 中使用 Log4Net 记录日志。  

2、实现IJob

TimeJob.cs实现IJob,在Execute方法中编写要处理的业务逻辑,系统就会按照Quartz的配置,定时处理

using log4net;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QuartzSDS.QuartzJobs
{
    public sealed class TimeJob : IJob
    {
        private readonly ILog _logger = LogManager.GetLogger(typeof(TimeJob));

        public void Execute(IJobExecutionContext context)
        {
            _logger.InfoFormat("执行TimeJob业务");
            Console.WriteLine($"执行TimeJob业务");
        }
    }
}

3、使用Topshelf调度任务

Topshelf调度可以查看某大佬的使用介绍,以下是文章地址 :
https://www.cnblogs.com/jys509/p/4614975.html

using System;
using Common.Logging;
using Quartz.Impl;
using Topshelf;

namespace QuartzSDS
{
	/// <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 void Initialize()
		{
			try
			{				
				schedulerFactory = CreateSchedulerFactory();
				scheduler = GetScheduler();
			}
			catch (Exception e)
			{
				logger.Error("Server initialization failed:" + e.Message, e);
				throw;
			}
		}

        /// <summary>
        /// Gets the scheduler with which this server should operate with.
        /// </summary>
        /// <returns></returns>
	    protected virtual 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
	    {
	        get { return 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;
	    }
	}
}

4、程序入口

using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Timers;
using Topshelf;

namespace QuartzSDS
{
 
    class Program
    {
        static void Main(string[] args)
        {
            log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(AppDomain.CurrentDomain.BaseDirectory + "log4net.config"));
            HostFactory.Run(x =>
            {
                x.UseLog4Net();

                x.Service<QuartzServer>();
                x.SetDescription("QuartzSDS集成平台调用");
                x.SetDisplayName("QuartzSDS");
                x.SetServiceName("QuartzSDS集成平台接口调用");

                x.EnablePauseAndContinue();
            });
        }
    }
}

5、配置quartz.config、quartz_jobs.xml、log4net.config

说明:这三个文件,分别选中→右键属性→复制到输入目录设为:始终复制,如图:
在这里插入图片描述
quartz.config文件

# You can configure your scheduler in either <quartz> configuration section
# or in quartz properties file
# Configuration section has precedence

quartz.scheduler.instanceName = QuartzTest

# 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 = ~/quartzJobs.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

quartzJobs.xml

<?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(必填) 任务名称,同一个group中多个job的name不能相同,若未设置group则所有未设置group的job为同一个分组,如:<name>sampleJob</name>-->
			<name>TimeJob</name>
			<!--group(选填) 任务所属分组,用于标识任务所属分组,如:<group>sampleGroup</group>-->
			<group>Time</group>
			<!--description(选填) 任务描述,用于描述任务具体内容,如:<description>Sample job for Quartz Server</description>-->
			<description>TimeJob执行调度</description>
			<!--job-type(必填) 任务类型,任务的具体类型及所属程序集,格式:实现了IJob接口的包含完整命名空间的类名,程序集名称,
      如:<job-type>Quartz.Server.SampleJob, Quartz.Server</job-type>-->
			<job-type>QuartzSDS.QuartzJobs.TimeJob,QuartzSDS</job-type>
			<!--durable(选填) 具体作用不知,官方示例中默认为true,如:<durable>true</durable>-->
			<durable>true</durable>
			<!--recover(选填) 具体作用不知,官方示例中默认为false,如:<recover>false</recover>-->
			<recover>false</recover>
		</job>
		<trigger>
			<cron>
				<!--name(必填) 触发器名称,同一个分组中的名称必须不同-->
				<name>TimeJobTrigger</name>
				<!--group(选填) 触发器组-->
				<group>Time</group>
				<!--description(选填) 触发器描述-->
				<description>TimeJob</description>
				<!--job-name(必填) 要调度的任务名称,该job-name必须和对应job节点中的name完全相同-->
				<job-name>TimeJob</job-name>
				<!--job-group(选填) 调度任务(job)所属分组,该值必须和job中的group完全相同-->
				<job-group>Time</job-group>
				<!--start-time(选填) 任务开始执行时间utc时间,北京时间需要+08:00,如:<start-time>2012-04-01T08:00:00+08:00</start-time>
      表示北京时间2012年4月1日上午8:00开始执行,注意服务启动或重启时都会检测此属性,若没有设置此属性或者start-time设置的时间比当前时间较早,
      则服务启动后会立即执行一次调度,若设置的时间比当前时间晚,服务会等到设置时间相同后才会第一次执行任务,一般若无特殊需要请不要设置此属性-->
				<start-time>2023-04-01T00:00:00+08:00</start-time>
				<!--am12点执行,pm12点执行-->
				<cron-expression>* * 0,12 * * ? </cron-expression>
			</cron>
		</trigger>

	</schedule>
</job-scheduling-data>

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= "E:QuartzSDSLoglog"/>
      <!--是否是向文件中追加日志-->
      <param name= "AppendToFile" value= "true"/>
      <!--log保留天数-->
      <param name= "MaxSizeRollBackups" value= "10"/>
      <!--日志文件名是否是固定不变的-->
      <param name= "StaticLogFileName" value= "false"/>
      <!--日志文件名格式为:2008-08-31.log-->
      <param name= "DatePattern" value= "yyyy-MM-dd&quot;.read.log&quot;"/>
      <!--日志根据日期滚动-->
      <param name= "RollingStyle" value= "Date"/>
      <layout type="log4net.Layout.PatternLayout">
        <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n %loggername" />
      </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>

6、运行项目,以上quartzJobs.xml文件中cron-expression设置的是中午12点执行一次,凌晨12点执行一次

在这里插入图片描述

7、安装、启动、卸载服务命令

1、以管理身份运行cmd
2、例如此项目是在我电脑e盘下:
安装服务:
E:Quartz.Net集成平台接口调用QuartzSDSQuartzSDSinDebugQuartzSDS.exe  install

启动服务:
E:Quartz.Net集成平台接口调用QuartzSDSQuartzSDSinDebugQuartzSDS.exe  start

卸载服务:
E:Quartz.Net集成平台接口调用QuartzSDSQuartzSDSinDebugQuartzSDS.exe  uninstall

在这里插入图片描述

quartz_jobs.xml详解:

  1. <job-scheduling-data>:这是根节点,它定义了 XML 文件的命名空间和版本号,同时包含了一个或多个 <schedule> 节点。

    • xmlns:指定了 XML 文件的命名空间为 http://quartznet.sourceforge.net/JobSchedulingData

    • xmlns:xsi:指定了 XML 文件中使用的 XML Schema 命名空间,用于指定 XML 文件的格式。

    • version:指定了 XML 文件的版本号,当前版本为 2.0。

  2. <schedule>:这个节点包含了作业和触发器的定义。

    • <job>:这个节点定义了一个作业。

      • <name>:作业的名称,必须唯一。

      • <group>:作业所属的组,可以用来对作业进行分组管理。

      • <description>:作业的描述信息,可以用来描述作业的用途等。

      • <job-type>:作业的类型,是一个类的完全限定名和程序集名称的组合,用于指定作业的实现类。

        例如,在上面的示例中,作业的类型为 MyNamespace.MyJob,表示在 MyAssembly 程序集中有一个名为 MyNamespace.MyJob 的类实现了该作业。

    • <trigger>:这个节点定义了一个触发器。

      • <cron>:这个节点表示使用 Cron 表达式来指定触发器的执行时间。

        • <name>:触发器的名称,必须唯一。

        • <group>:触发器所属的组,可以用来对触发器进行分组管理。

        • <description>:触发器的描述信息,可以用来描述触发器的用途等。

        • <job-name>:触发器关联的作业的名称。

        • <job-group>:触发器关联的作业所属的组。

        • <cron-expression>:Cron 表达式,用于指定触发器的执行时间。Cron 表达式是由 7 个字段组成的,分别表示秒、分、时、日、月、周、年等时间信息。

          例如,在上面的示例中,Cron 表达式 0 0/5 * * * ? 表示每隔 5 分钟执行一次。

在配置文件中,您可以定义多个作业和触发器,以便根据配置文件中的设置执行多个作业。在加载配置文件时,Quartz.NET 将会根据作业和触发器的定义创建对应的作业和触发器,并将它们添加到调度器中,以便按照指定的时间表执行作业。

Quartz的cron表达式:
官方地址:https://www.quartz-scheduler.net/documentation/quartz-2.x/tutorial/crontrigger.html#introduction
在这里插入图片描述
Cron 表达式是一种用于指定定期执行任务的时间表达式,它由 7 个字段组成,分别表示秒、分、时、日、月、周、年等时间信息。

Cron 表达式的一般格式为:

<秒> <分> <时> <日> <月> <周> <年>

其中,<秒><分><时><日><月><周><年> 都是 0 到 59 或者 0 到 23 之间的整数值,具体含义如下:

  • <秒>:表示定时任务的秒数,取值范围为 0 到 59。

  • <分>:表示定时任务的分钟数,取值范围为 0 到 59。

  • <时>:表示定时任务的小时数,取值范围为 0 到 23。

  • <日>:表示定时任务的日期,取值范围为 1 到 31。

  • <月>:表示定时任务的月份,取值范围为 1 到 12。

  • <周>:表示定时任务的星期几,取值范围为 0 到 7,其中 0 和 7 都表示星期日。

  • <年>:表示定时任务的年份,取值范围为 1970 到 2099。

Cron 表达式中还可以使用特殊字符来表示一些特殊的时间规则,包括:

  • *:表示匹配任意值,比如 * * * * * * 表示每秒都执行一次任务。

  • ?:表示不指定值,用于代替 <日><周> 中的一个,比如 0 0 12 ? * MON-FRI 表示每周一到周五中午 12 点执行一次任务。

  • -:表示指定一个范围,比如 0 0 0 1-31 * * 表示每个月的 1 号到 31 号的零点都执行一次任务。

  • ,:表示列出多个值,比如 0 0 0 * * MON,WED 表示每周一和周三的零点都执行一次任务。

  • /:表示指定一个步长,比如 0/5 * * * * * 表示每隔 5 秒钟执行一次任务。

注意,Cron 表达式中的所有时间都是基于调度器所在的服务器的本地时间而定的。如果需要使用其他时区的时间,可以在配置文件中指定时区信息。
以上是使用Quartz.net + Topshelf完成服务调用,如有错误可指正 Thanks♪(・ω・)ノ

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。