使用Topshelf+Quartz开发Windows定时任务

使用Topshelf开发Windows服务,比起直接写Windows服务的优势在于,可以直接运行调试。开发完成后,直接使用install命令注册为服务即可,简单高效。

一、准备工作

  1. 创建一个控制台应用


    image.png
  2. 在Nuget包管理器中搜索并安装Topshelf、Quartz、log4net这几个库


    image.png

使用Topshelf框架

  1. 在程序入口类Program.cs中修改Main方法
using Topshelf;

namespace DemoService
{
    class Program
    {
        static void Main(string[] args)
        {
            string serviceName = "DemoService";
            HostFactory.Run(config =>
            {
                config.Service<StartService>(s =>
                {
                    s.ConstructUsing(name => new StartService());
                    s.WhenStarted(service => service.Start());
                    s.WhenStopped(service => service.Stop());
                });

                config.RunAsLocalSystem();
                config.SetDescription(serviceName);
                config.SetServiceName(serviceName);
                config.SetDisplayName(serviceName);
            });
        }
    }
}
  1. 新增类文件:StartService.cs,并编写Start()、Stop()方法,这两个方法在Main方法中被注册为服务的启动和停止方法。
namespace DemoService
{
    public class StartService
    {
        public void Start()
        {
        }
        public void Stop()
        {
        }
    }
}

二、配置日志工具log4net

  1. 创建一个应用程序配置文件,改名为log4net.config

    image.png

  2. log4net.config<configuration>项中写入如下配置项

  <!-- Level的级别,由高到低 --> 
  <!-- None > Fatal > ERROR > WARN > DEBUG > INFO > ALL--> 
  <!-- 解释:如果level是ERROR,则在cs文件里面调用log4net的info()方法,则不会写入到日志文件中-->
  <log4net>
    <!--错误日志类-->
    <logger name="logerror"><!--日志类的名字-->
      <level value="ALL" /><!--定义记录的日志级别-->
      <appender-ref ref="ErrorAppender" /><!--记录到哪个介质中去-->
    </logger>
    <!--信息日志类-->
    <logger name="loginfo">
      <level value="ALL" />
      <appender-ref ref="InfoAppender" />
    </logger>
    <!--错误日志附加介质-->
    <appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender"><!-- name属性指定其名称,type则是log4net.Appender命名空间的一个类的名称,意思是,指定使用哪种介质-->
      <param name="File" value="Log\\LogError\\" /><!--日志输出到exe程序这个相对目录下-->
      <param name="AppendToFile" value="true" /><!--输出的日志不会覆盖以前的信息-->
      <param name="MaxSizeRollBackups" value="100" /><!--备份文件的个数-->
      <param name="MaxFileSize" value="10240" /><!--当个日志文件的最大大小-->
      <param name="StaticLogFileName" value="false" /><!--是否使用静态文件名-->
      <param name="DatePattern" value="&quot;Log_&quot;yyyyMMdd&quot;.htm&quot;" /><!--日志文件名-->
      <param name="RollingStyle" value="Date" /><!--文件创建的方式,这里是以Date方式创建-->
      <!--错误日志布局-->
      <layout type="log4net.Layout.PatternLayout">
        <param name="ConversionPattern" value="&lt;HR COLOR=red&gt;%n异常时间:%d [%t] &lt;BR&gt;%n异常级别:%-5p &lt;BR&gt;%n异 常 类:%c [%x] &lt;BR&gt;%n%m &lt;BR&gt;%n &lt;HR Size=1&gt;"  />
      </layout>
    </appender>
    <!--信息日志附加介质-->
    <appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
      <param name="File" value="Log\\LogInfo\\" />
      <param name="AppendToFile" value="true" />
      <param name="MaxFileSize" value="10240" />
      <param name="MaxSizeRollBackups" value="100" />
      <param name="StaticLogFileName" value="false" />
      <param name="DatePattern" value="&quot;Log_&quot;yyyyMMdd&quot;.htm&quot;" />
      <param name="RollingStyle" value="Date" />
      <!--信息日志布局-->
      <layout type="log4net.Layout.PatternLayout">
        <param name="ConversionPattern" value="&lt;HR COLOR=blue&gt;%n日志时间:%d [%t] &lt;BR&gt;%n日志级别:%-5p &lt;BR&gt;%n日 志 类:%c [%x] &lt;BR&gt;%n%m &lt;BR&gt;%n &lt;HR Size=1&gt;"  />
      </layout>
    </appender>
  </log4net>
  1. 以上配置将会将Info日志保存至项目文件夹下的Log\\LogInfo\\,将错误日志保存至Log\\LogError\\,日志文件保存为.htm格式。

    转义字符对照表

    日志效果图

  2. Properties下的AssemblyInfo.cs中注册一下log4net.config配置文件

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", ConfigFileExtension = "config", Watch = true)]
  1. 编写LogHelper.cs
    public class LogHelper
    {
        //这里的 loginfo 和 log4net.config 里的名字要一样
        private static readonly log4net.ILog loginfo = log4net.LogManager.GetLogger("loginfo");
        //这里的 logerror 和 log4net.config 里的名字要一样
        private static readonly log4net.ILog logerror = log4net.LogManager.GetLogger("logerror");
        public static void Info(string message)
        {
            if (loginfo.IsInfoEnabled)
            {
                loginfo.Info(message);
            }
        }

        public static void Error(string message, Exception ex)
        {
            if (logerror.IsErrorEnabled)
            {
                logerror.Error(message, ex);
            }
        }
    }

三、使用Quartz编写定时任务

  1. 创建一个DemoJob.cs
using Quartz;
using System;

namespace DemoService.Jobs
{
    public class DemoJob : IJob
    {
        public void Execute(IJobExecutionContext context)
        {
            Console.WriteLine("Hello");
            LogHelper.Info("Hello");
        }
    }
}
  1. 使用Quartz框架,打开StartService.cs文件,在Start()方法中添加如下内容
    public class StartService
    {
        public void Start()
        {
            // 开启调度
            ISchedulerFactory sf = new StdSchedulerFactory();
            IScheduler scheduler = sf.GetScheduler();
            IJobDetail job = JobBuilder.Create<DemoJob>().Build();
            // 服务启动时执行一次
            // ITrigger triggerNow = TriggerBuilder.Create().StartNow().Build();

            // 每日22点执行一次
            ITrigger trigger = TriggerBuilder.Create()
                .StartNow()
                .WithCronSchedule("0 0 22 1/1 * ? ")
                .Build();
            
            scheduler.ScheduleJob(job, trigger);
            scheduler.Start();
            LogHelper.Info("服务开始执行。");
        }

       public void Stop()
        {

        }
    }
  1. Cron表达式很强大,但刚上手需要一定的学习成本,此处附上一个生成表达式的工具:
    在线Cron表达式生成器传送门

  2. 想了解Cron 表达式,可参考该篇博文:https://www.cnblogs.com/yanghj010/p/10875151.html

四、服务注册、卸载

编写如下两个批处理文件,放在生成的目录下,需要注册、卸载时,直接执行即可。

  1. install.bat
cd /d "%~dp0"
生成的可执行文件名.exe install
net start DemoService
cmd
  1. uninstall.bat
cd /d "%~dp0"
生成的可执行文件名.exe uninstall
cmd
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容