使用WorkService创建定时任务

引用:

.NET 中的 Worker Service 入门介绍 - 技术译民 - 博客园 (cnblogs.com)

在 ASP.NET Core和Worker Service中使用Quartz.Net - 码农译站 - 博客园 (cnblogs.com)

新建 Worker Services

新建 Worker Services

添加Nlog

手动或使用NuGet在csproj中添加依赖项

  • 安装最新版本:
  • NLog.Web.AspNetCore 4.9+
  • 如有可能,更新NLog软件包

创建nlog.config文件。

在项目的根目录中创建nlog.config(全部小写)文件。
我们使用以下示例:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <!-- enable asp.net core layout renderers -->
  <extensions>
    <add assembly="NLog.Web.AspNetCore"/>
  </extensions>

  <!-- the targets to write to -->
  <targets>
    <!--写入文件-->
    <target
     xsi:type="File"
     name="DebugFile"
     fileName="Logs\Debug\${shortdate}.log"
     layout="日志时间:${longdate}${newline}日志来源:${callsite}${newline}日志级别:${uppercase:${level}}${newline}消息内容:${message}${newline}----------------------------------------------------------------${newline}" >
    </target>
    <target
      xsi:type="File"
      name="InfoFile"
      fileName="Logs\Info\${shortdate}.log"
      layout="日志时间:${longdate}${newline}日志来源:${callsite}${newline}日志级别:${uppercase:${level}}${newline}消息内容:${message}${newline}----------------------------------------------------------------${newline}" >
    </target>
    <target
      xsi:type="File"
      name="ErrorFile"
      fileName="Logs\Error\${shortdate}.log"
      layout="日志时间:${longdate}${newline}日志来源:${callsite}${newline}日志级别:${uppercase:${level}}${newline}消息内容:${message}${newline}异常信息:${exception:format=tostring}${newline}----------------------------------------------------------------${newline}" >
    </target>
  </targets>

  <!-- rules to map from logger name to target -->
  <rules>
    <!--Skip non-critical Microsoft logs and so log only own logs-->
    <logger name="Microsoft.*" maxlevel="Info" final="true" />
    <logger name="*" minlevel="Debug" maxLevel="Debug" writeTo="DebugFile" />
    <logger name="*" minlevel="Info" maxLevel="Info" writeTo="InfoFile" />
    <logger name="*" minlevel="Error" maxLevel="Error" writeTo="ErrorFile" />
  </rules>
</nlog>

注意:日志生成的文件在bin目录下

配置文件的更多详细信息在这里

请注意,如果删除所有其他LoggingProviders(如控制台)并且仅使用NLog,则可能必须特别注意Hosting Lifetime Startup Messages。因为这可能导致托管环境(Visual Studio / Docker / Azure容器)看不到启动的应用程序。

启用复制到bin文件夹

  • .csproj手动编辑文件并添加:
    <ItemGroup>
        <Content Update="nlog.config" CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>
    

修改 program.cs

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Web;

namespace DemoWorkerService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
            try
            {
                logger.Debug("init main");
                CreateHostBuilder(args).Build().Run();
            }
            catch (Exception exception)
            {
                //NLog: catch setup errors
                logger.Error(exception, "Stopped program because of exception");
                throw;
            }
            finally
            {
                // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
                NLog.LogManager.Shutdown();
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>();
                }).ConfigureLogging(logging =>
                {
                    logging.ClearProviders();
                    logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
                })
.UseNLog();  // NLog: Setup NLog for Dependency injection;
    }
}

配置 appsettings.json

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Trace",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

写日志

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace DemoWorkerService
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

运行后日志不在控制台显示

注意:日志生成的文件在bin目录下

使用Quartz.Net

安装Quartz.Net

手动或使用NuGet在csproj中添加依赖项

  • 安装最新版本:

    Microsoft.Extensions.Hosting 5.0+

    Quartz.Extensions.Hosting 3.3.3+

  • 如有可能,更新Quartz软件包

使用Quartz.Net

  • 创建ServiceCollectionQuartzConfiguratorExtensions.cs拓展文件

    using Microsoft.Extensions.Configuration;
    using Quartz;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace DemoWorkerService
    {
        public static class ServiceCollectionQuartzConfiguratorExtensions
        {
            public static void AddJobAndTrigger<T>(
            this IServiceCollectionQuartzConfigurator quartz,
            IConfiguration config)
            where T : IJob
            {
                // Use the name of the IJob as the appsettings.json key
                string jobName = typeof(T).Name;
    
                // Try and load the schedule from configuration
                var configKey = $"Quartz:{jobName}";
                var cronSchedule = config[configKey];
    
                // Some minor validation
                if (string.IsNullOrEmpty(cronSchedule))
                {
                    throw new Exception($"No Quartz.NET Cron schedule found for job in configuration at {configKey}");
                }
    
                // register the job as before
                var jobKey = new JobKey(jobName);
                quartz.AddJob<T>(opts => opts.WithIdentity(jobKey));
    
                quartz.AddTrigger(opts => opts
                    .ForJob(jobKey)
                    .WithIdentity(jobName + "-trigger")
                    .WithCronSchedule(cronSchedule)); // use the schedule from configuration
            }
        }
    }
    
    
  • 创建HelloWorldJob.cs任务文件

    using Microsoft.Extensions.Logging;
    using Quartz;
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DemoWorkerService.Jobs
    {
        [DisallowConcurrentExecution]
        public class HelloWorldJob : IJob
        {
            private readonly ILogger<HelloWorldJob> _logger;
            public HelloWorldJob(ILogger<HelloWorldJob> logger)
            {
                _logger = logger;
            }
    
            public Task Execute(IJobExecutionContext context)
            {
                _logger.LogInformation("Hello world!");
                return Task.CompletedTask;
            }
        }
    }
    
    
  • 添加服务

     public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .UseWindowsService()
                    .ConfigureServices((hostContext, services) =>
                    {
                        // Add the required Quartz.NET services
                        services.AddQuartz(q =>
                        {
                            q.UseMicrosoftDependencyInjectionJobFactory();
    		
    						// Register the job, loading the schedule from configuration
                            q.AddJobAndTrigger<HelloWorldJob>(hostContext.Configuration);
                        });
    
                        // WaitForJobsToComplete:此设置确保当请求关闭时,Quartz.NET在退出之前等待作业优雅地结束。
                        services.AddQuartzHostedService(
                            q => q.WaitForJobsToComplete = true);
    
                        // other config
                    }).ConfigureLogging(logging =>
                    {
                        logging.ClearProviders();
                        logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
                    })
                    .UseNLog();  // NLog: Setup NLog for Dependency injection;
    

    文件将不会打印再控制器台中

  • 添加配置文件再appsettings.json

    "Quartz": {
    	"HelloWorldJob": "0/5 * * * * ?"
    }
    

添加 Windows Services 依赖

为了作为 Windows 服务运行,我们需要我们的 Worker 监听来自 ServiceBase 的启动和停止信号,ServiceBase 是将 Windows 服务系统公开给 .NET 应用程序的 .NET 类型。为此,我们需要添加 Microsoft.Extensions.Hosting.WindowsServices NuGet 包:

dotnet add package Microsoft.Extensions.Hosting.WindowsServices

然后修改 Program.cs 中的 CreateHostBuilder 方法,添加 UseWindowsService 方法调用:

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseWindowsService()
                .ConfigureServices((hostContext, services) =>
                {
                    
                }).ConfigureLogging(logging =>
                {
                    logging.ClearProviders();
                    logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
                })
                .UseNLog();  // NLog: Setup NLog for Dependency injection;

发布程序

dotnet publish -c Release -r win-x64 -o D:\DemoWorkerService

D:\DemoWorkerService为发布后的地址

创建并运行任务

当我们使用 sc.exe 实用工具管理服务时,必须以管理员身份运行 Windows 命令提示符,否则会执行失败。

  • 安装服务

    sc create 我的服务名称 binPath= "D:\DemoWorkerService\DemoWorkerService.exe" start= auto displayname= "服务描述"
    

    1、每个命令行选项 (参数) 必须包含等号作为选项名称的一部分。
    2、选项与其值之间必须有一个空格(例如,type= own),如果遗漏了空格,操作将失败。

    sc create
    
    描述:
            在注册表和服务数据库中创建服务项。
    用法:
            sc <server> create [service name] [binPath= ] <option1> <option2>...
    
    选项:
    注意: 选项名称包括等号。
          等号和值之间需要一个空格。
     type= <own|share|interact|kernel|filesys|rec|userown|usershare>
           (默认 = own)
     start= <boot|system|auto|demand|disabled|delayed-auto>
           (默认 = demand)
     error= <normal|severe|critical|ignore>
           (默认 = normal)
     binPath= <.exe 文件的 BinaryPathName>
     group= <LoadOrderGroup>
     tag= <yes|no>
     depend= <依存关系(以 / (斜杠)分隔)>
     obj= <AccountName|ObjectName>
           (默认= LocalSystem)
     DisplayName= <显示名称>
     password= <密码>
    
  • 设置服务描述

    sc description
    描述:
            设置服务的描述字符串。
    用法:
            sc <server> description [service name] [description]
    
    sc description 我的服务名称 "服务描述"
    
  • 启动服务

    sc start 我的服务名称
    
  • 停止服务

    sc stop 我的服务名称
    
  • 删除服务

    sc delete 我的服务名称
    
原文地址:https://www.cnblogs.com/qs315/p/15593849.html