使用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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容