Quartz.Net 3.0.7.0+Topshelf使用心得

首先nuget下载Topshelf 4.2.1.215和Quartz 3.0.7.0

创建入口

     class Program
    {
        static void Main(string[] args)
        {
            // 配置和运行宿主服务
            HostFactory.Run(x =>
            {
                x.Service<QuartzServiceRunner>(s =>
                {
                    s.ConstructUsing(name => new QuartzServiceRunner());
                    s.WhenStarted(m => m.Start());
                    s.WhenStopped(m => m.Stop());
                    s.WhenPaused(m => m.Pause());
                    s.WhenContinued(m => m.Continue());
                });
                ////服务器使用内置账户运行
                x.RunAsLocalService();
                //// 服务自动启动(延时启动) -- 需要 .NET 4.0 或以上版本
                x.StartAutomaticallyDelayed();
                // 服务描述信息
                x.SetDescription(AppConfigHelper.GetAppSetting("ServiceDescription") ?? "油联网后台服务");
                // 服务显示名称
                x.SetDisplayName(AppConfigHelper.GetAppSetting("ServiceDisplayName") ?? "油联网后台服务");
                // 服务名称
                x.SetServiceName(AppConfigHelper.GetAppSetting("ServiceName") ?? "油联网后台服务");
                x.EnablePauseAndContinue();

            });
            Console.ReadKey();
        }
    }

config配置

  <!--topshelf服务配置-->
   <add key="ServiceName" value="Demo.Services"/>
   <add key="ServiceDescription" value="后台服务Demo"/>
   <add key="ServiceDisplayName" value="Demo.Services"/>

开始执行QuartzServiceRunner

RunProgram方法中的线程池和远程输出配置用于之后的作业远程管理

本demo通过xml的方式实现灵活的任务配置调度,该xml为quartz_jobs.xml

 public class QuartzServiceRunner
    {
        //创建调度器工厂
        ISchedulerFactory factory = new StdSchedulerFactory();
        //创建调度器
        IScheduler scheduler = null;
        public QuartzServiceRunner()
        {
            RunProgram().GetAwaiter().GetResult();
        }

        private async Task RunProgram()
        {
            var properties = new NameValueCollection();
            properties["quartz.scheduler.instanceName"] = "RemoteServerSchedulerClient";

            // 设置线程池
            properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
            properties["quartz.threadPool.threadCount"] = "5";
            properties["quartz.threadPool.threadPriority"] = "Normal";

            // 远程输出配置
            properties["quartz.scheduler.exporter.type"] = "Quartz.Simpl.RemotingSchedulerExporter, Quartz";
            properties["quartz.scheduler.exporter.port"] = AppConfigHelper.GetAppSetting("ExporterPort") ?? "82";
            properties["quartz.scheduler.exporter.bindName"] = "QuartzScheduler";
            properties["quartz.scheduler.exporter.channelType"] = "tcp";
            factory = new StdSchedulerFactory(properties);
            XMLSchedulingDataProcessor processor = new XMLSchedulingDataProcessor(new SimpleTypeLoadHelper());
            scheduler =await factory.GetScheduler();
            await processor.ProcessFileAndScheduleJobs("~/quartz_jobs.xml", scheduler);
            //myJobListener监控所有的job
            scheduler.ListenerManager.AddJobListener(new JobListener(), GroupMatcher<JobKey>.AnyGroup());
        }


        public async void Start()
        {
            LogHelper.Info("", "--------------------------------------------------------");
            LogHelper.Info("", "服务开始执行");
            //开始执行
            await scheduler.Start();
        }

        public async void Stop()
        {
            LogHelper.Info("", "服务停止执行");
            //shutdown(true)表示等待所有正在执行的任务执行完毕后关闭Scheduler
            //shutdown(false),即shutdown()表示直接关闭Scheduler
            await scheduler.Shutdown(false);
        }

        public async void Pause()
        {
            LogHelper.Info("", "服务暂停执行");
            //暂停
            await scheduler.PauseAll();
        }

        public async void Continue()
        {
            LogHelper.Info("", "服务继续执行");
            //继续
            await scheduler.ResumeAll();
        }
    }

任务job基类 BaseJob

该基类的主要作用是记录任务的执行记录和异常处理

 public abstract class BaseJob : IJob
    {
        public abstract Task ExecuteJob(IJobExecutionContext context);

        public async Task Execute(IJobExecutionContext context)
        {
            var fullName = context.JobDetail.JobType.FullName;
            try
            {
                LogHelper.Info("", fullName + ":开始执行");
                await Console.Out.WriteLineAsync($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}:{fullName}:开始执行");
                await ExecuteJob(context);
                LogHelper.Info("", fullName + ":执行结束");
                await Console.Out.WriteLineAsync($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}:{fullName}:执行结束");
            }
            catch (Exception ex)
            {
                JobExecutionException e = new JobExecutionException(ex);
                await Console.Out.WriteLineAsync(fullName + ":" + e.Message);
                LogHelper.Error("", fullName + ":" + e.Message);
                // true 表示立即重新执行作业
                // e.RefireImmediately = false;
                //true 表示 Quartz 会自动取消所有与这个 job 有关的 trigger,从而避免再次运行 job
                e.UnscheduleAllTriggers = true;
                LogHelper.Info("", $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}:{fullName}:将停止执行");
                new WechatWorkApiBLL().SendLogs($"{fullName}将停止执行,错误详情:{e.Message}");
                throw e;
            }
        }
    }

quartz_jobs.xml配置

cron表达式可在http://www.bejson.com/othertools/cron/查询或生成

<?xml version="1.0" encoding="utf-8" ?>
<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>
    <!--Demo-->
    <!-- job 作业节点,每个作业则对应一个 job 节点。-->
    <job>
      <!--作业名称,同一个 group 中作业名称不能相同。-->
      <name>DemoJob</name>
      <!--作业分组名称,表示该作业所属分组。-->
      <group>DemoJobGroup</group>
      <!-- 作业描述,用于描述该作业的具体功能。-->
      <description>DemoJobjob</description>
      <!-- 指定作业将调用的作业实现类,格式为:命名空间.类名,程序集名称-->
      <job-type>HSIOT.Basic.QuartzService.App_Jobs.DemoJob,HSIOT.Basic.QuartzService</job-type>
      <!--默认为true-->
      <durable>true</durable>
      <!--默认为false-->
      <recover>false</recover>
    </job>
    <!--trigger 作业触发器节点,用于定义指定的作业以何种方式触发,一个作业可以有多个触发器,而每个触发器都独立执行调度。-->
    <trigger>
      <!--cron复杂任务触发器,使用cron表达式定制任务调度-->
      <cron>
        <!--触发器名称-->
        <name>DemoTrigger</name>
        <!-- 触发器分组名称-->
        <group>DemoGroup</group>
        <!-- 要调度的作业名称-->
        <job-name>DemoJob</job-name>
        <!--要调度的作业分组名称-->
        <job-group>DemoJobGroup</job-group>
        <!--cron 表达式 ;该节点为必须,如果省略整个服务将不能正常运行!可以在 http://www.bejson.com/othertools/cron/ 生成或查询-->
        <cron-expression>0/10 * * * * ? </cron-expression>
        <!--每10秒运行一次-->
      </cron>
    </trigger>    
  </schedule>
</job-scheduling-data>

DemoJob

 public class DemoJob : BaseJob
    {
        public override async Task ExecuteJob(IJobExecutionContext context)
        {
           await  Console.Out.WriteLineAsync($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}:Hello QuartzNet...");
        }
    }

运行截图

TopShelf服务安装

进入 Debug 文件夹,找到程序的.exe文件,管理员身份启动命令窗口,Demo.Services.exe install命令,即可发布该服务到本地

远程管理可参考这里 https://www.cnblogs.com/mushroom/p/4067558.html

posted @ 2019-10-08 10:38  来日可期0521  阅读(390)  评论(0)    收藏  举报