.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 我的服務名稱