在.NET CORE 3中使用Quartz.NET与Topshelf

之前做windows服务,使用的是Timer计时器来开发,做简单的事情也还行,但做复杂的,还是有点麻烦,所以考虑用Topshelf与Quartz.NET来简化一下。

Quartz.NET是一个强大、开源、轻量的作业调度框架,在项目中用来处理后台处理的任务,例如定时发送邮件通知、后台处理耗时的数据处理等,但在IIS部署的网站中应当注意应用程序池回收的问题。在所有.NET环境中都可以执行,包括但不限于winform、wpf、asp.net webform、asp.net mvc、控制台应用程序、windows服务,.net core等等

创建一个.net core 控制台应用程序,用NuGet包管理器安装Quartz、Topshelf、Topshelf.Log4Net,如下图

 先上一个Quartz的基本写法

    internal class Program
    {        static async Task Main(string[] args)
        {
            //创建作业调度池
            var factory = new StdSchedulerFactory();
            var scheduler = await factory.GetScheduler();
            //创建出一个具体的作业
            var job = JobBuilder.Create<GreetingJob>().Build();
            //配置一个触发器
            var trigger = (ISimpleTrigger)TriggerBuilder.Create()
                .WithSimpleSchedule(x => x.WithIntervalInSeconds(3).WithRepeatCount(int.MaxValue)).Build();
            //加入作业调度池中
            await scheduler.ScheduleJob(job, trigger);
            //开始运行
            await scheduler.Start();
            Console.ReadKey();
        }
    }
   [DisallowConcurrentExecution] //禁止并发执行
    public class GreetingJob : IJob
    {
        //private readonly ILog _logger = LogManager.GetLogger(typeof(GreetingJob));
        public Task Execute(IJobExecutionContext context)
        {
            Console.WriteLine($"{DateTime.Now}:你好吗?----From GreetingJob");
            //_logger.Info($"{DateTime.Now}:你好吗?----From GreetingJob");
            Thread.Sleep(3000);
            return Task.FromResult(true);
        }
    }

跑起来就是这个造型,3秒执行一次

 

 

 在Quartz.net中有两种触发器类型,一种是简单触发器,也就是上面的ISimpleTrigger接口,还有一种是Cron类型的触发器,对应的接口名是ICronTrigger,他使用cron表达式来描述Job的触发事件,这样可以处理更加复杂一些的需求,比如要每天在特定时间点执行(上午 10:00 及 下午 6:00),或者是特定日期执行 (每月5号) 等特殊需求,而这个就是 Quartz.Net 的强项啦,透过其 cron 来描述作业被触发的週期,从秒、分、时、日、月、星期、年都可以进行操作。

"10,20,25 * * * * ? *":每分钟的第10、20、25秒会执行

"10 0/5 * * * ?":每5分钟的第10秒会执行 (ex. 10:00:10 am, 10:05:10 am ...)

"0 20 10-13 ? * WED,FRI":每星期三与星期五的 10:20, 11:20, 12:20, 13:20 执行

"0 0/30 8-9 5,20 * ?":每月5号及20号的 8:00, 8:30, 9:00, 9:30 执行

遇到复杂的情境,无法使用单一表示式来定义,可以考虑定义多个 Trigger 来触发相同 Job 。

            var trigger = (ICronTrigger)TriggerBuilder.Create().WithIdentity("Main","Main")
                .WithCronSchedule("10,20,25 * * * * ? *").StartAt(DateTime.UtcNow).WithPriority(1).Build();

直接上加了Topshelf的代码,饭点吃饭去了,你品,你细细的品......

using log4net;
using log4net.Repository;
using Quartz;
using Quartz.Impl;
using System;
using System.Threading;
using System.Threading.Tasks;
using Topshelf;

namespace QuartzConsoleApp
{
    internal class Program
    {
        private static ILoggerRepository _loggerRepository= LogManager.CreateRepository("rmb");
        //static async Task Main(string[] args)
        static void Main(string[] args)
        {
            try
            {
                // 配置和运行宿主服务
                HostFactory.Run(x =>
                {
                    x.UseLog4Net("App.config");
                    x.Service<ServiceRunner>(s =>
                    {
                        // 指定服务类型。这里设置为 Service
                        s.ConstructUsing(name => new ServiceRunner());

                        // 当服务启动后执行什么
                        s.WhenStarted((sc, hc) => sc.Start(hc));

                        // 当服务停止后执行什么
                        s.WhenStopped((sc, hc) => sc.Stop(hc));
                    });

                    // 服务用本地系统账号来运行
                    x.RunAsLocalSystem();
                    //x.StartAutomaticallyDelayed();
                    x.StartAutomatically();

                    // 服务描述信息
                    x.SetDescription("测试Greeting服务,此处是服务描述信息");
                    // 服务显示名称
                    x.SetDisplayName("测试Greeting服务");
                    // 服务名称
                    x.SetServiceName("GreetingJobService");
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
    }

    [DisallowConcurrentExecution] //禁止并发执行
    public class GreetingJob : IJob
    {
        private readonly ILog _logger = LogManager.GetLogger(typeof(GreetingJob));
        public Task Execute(IJobExecutionContext context)
        {
            //Console.WriteLine($"{DateTime.Now}:你好吗?----From GreetingJob");
            _logger.Info($"{DateTime.Now}:你好吗?----From GreetingJob");
            Thread.Sleep(3000);
            return Task.FromResult(true);
        }
    }


    public sealed class ServiceRunner : ServiceControl, ServiceSuspend
    {
        //调度器
        private readonly IScheduler scheduler;

        public ServiceRunner()
        {
            scheduler = StdSchedulerFactory.GetDefaultScheduler().GetAwaiter().GetResult();
            //创建出一个具体的作业
            var job = JobBuilder.Create<GreetingJob>().Build();
            //配置一个触发器
            var trigger = (ISimpleTrigger)TriggerBuilder.Create()
                .WithSimpleSchedule(x => x.WithIntervalInSeconds(3).WithRepeatCount(int.MaxValue)).Build();
            //加入作业调度池中
            scheduler.ScheduleJob(job, trigger);
        }

        //开始
        public bool Start(HostControl hostControl)
        {
            scheduler.Start();
            return true;
        }

        //停止
        public bool Stop(HostControl hostControl)
        {
            scheduler.Shutdown(false);
            return true;
        }

        //恢复所有
        public bool Continue(HostControl hostControl)
        {
            scheduler.ResumeAll();
            return true;
        }

        //暂停所有
        public bool Pause(HostControl hostControl)
        {
            scheduler.PauseAll();
            return true;
        }

    }
}

配置文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <log4net>
    <appender name="ManagedColoredConsoleAppender" type="log4net.Appender.ManagedColoredConsoleAppender">
      <mapping>
        <level value="ERROR" />
        <foreColor value="Red" />
      </mapping>
      <mapping>
        <level value="Info" />
        <foreColor value="Green" />
      </mapping>
      <mapping>
        <level value="DEBUG" />
        <foreColor value="Blue" />
      </mapping>
      <mapping>
        <level value="WARN" />
        <foreColor value="Yellow" />
      </mapping>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%d{ABSOLUTE} [%thread] %-5p %c{1}:%L - %m%n" />
      </layout>
      <filter type="log4net.Filter.LevelRangeFilter">
        <param name="LevelMin" value="DEBUG" />
        <param name="LevelMax" value="Fatal" />
      </filter>
    </appender>
    <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <file value=".\logs\" />
      <datePattern value="'GreetingJobService_'yyyy.MM.dd'.log'" />
      <staticLogFileName value="false" />
      <appendToFile value="true" />
      <rollingStyle value="Composite" />
      <maxSizeRollBackups value="10" />
      <maximumFileSize value="5MB" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%d [%t] %-5p %c - %m%n" />
      </layout>
    </appender>
    <root>
      <level value="all" />
      <appender-ref ref="ManagedColoredConsoleAppender" />
      <appender-ref ref="RollingFile" />
    </root>
  </log4net>
</configuration>

 调试效果

 

 

部署、开始、卸载服务只需要一句命令行就可以:

    安装:你的程序.exe install
    启动:你的程序.exe start
    卸载:你的程序.exe uninstall
更多命令:你的程序.exe help

 

posted @ 2020-04-17 11:54  静文·辅神  阅读(1306)  评论(0编辑  收藏  举报