引用:
.NET 中的 Worker Service 入门介绍 - 技术译民 - 博客园 (cnblogs.com)
在 ASP.NET Core和Worker Service中使用Quartz.Net - 码农译站 - 博客园 (cnblogs.com)
新建 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 我的服务名称