c# - 如何创建仅侦听事件的 .NET 后台服务?
问题描述
我需要对某些服务器上的 Windows 事件进行一些相当精细的分析,并将它们转发到 syslog 服务器。我创建了一个运行良好的 .NET 服务,但其中有一些我不明白的方面。
这是 Program.cs,它几乎是开箱即用的,但我已经向它添加了一些配置内容:
using IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService(options =>
{
options.ServiceName = "LEULogSenderSVC";
})
.ConfigureServices((hostContext, services) =>
{
IConfiguration configuration = hostContext.Configuration;
LEULogConfig options = configuration.GetSection("LEULogConfig").Get<LEULogConfig>();
services.AddSingleton(options);
services.AddHostedService<LogMonitorSvc>();
})
.Build();
await host.RunAsync();
这是 LogMonitorSvc.cs(为简洁而编辑):
public sealed class LogMonitorSvc : BackgroundService
{
private readonly ILogger<LogMonitorSvc> _logger;
private static LEULogConfig _options;
private static MessageFiltering systemLogRules { get; set; }
private static MessageFiltering applicationLogRules { get; set; }
private static void OnApplicationEntryWritten(object source, EntryWrittenEventArgs e)
{
//Process Application Log Entry, optionally send to syslog...
}
private static void OnSystemEntryWritten(object source, EntryWrittenEventArgs e)
{
//Process System Log Entry, optionally send to syslog...
}
public LogMonitorSvc(ILogger<LogMonitorSvc> logger, LEULogConfig options)
{
_logger = logger;
_options = options;
EventLog systemLog = new EventLog("System", ".");
EventLog applicationLog = new EventLog("Application", ".");
systemLogRules = MessageFiltering.DeSerialize(options.SystemLogRulesFilePath);
systemLog.EntryWritten += new EntryWrittenEventHandler(OnSystemEntryWritten);
systemLog.EnableRaisingEvents = true;
applicationLogRules = MessageFiltering.DeSerialize(options.ApplicationLogRulesFilePath);
applicationLog.EntryWritten += new EntryWrittenEventHandler(OnApplicationEntryWritten);
applicationLog.EnableRaisingEvents = true;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
//_logger.LogWarning("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(10000, stoppingToken);
}
}
}
我发现的每个示例似乎都假设有一些事情会定期发生(几乎总是基于计时器的事件)导致“任务”运行。我不需要那个,我只需注册两个事件处理程序,并且服务应该只是冷却(它确实如此)直到其中一个事件发生,此时相应的 On...EntryWritten 处理程序运行。同样,这基本上是有效的,但感觉很笨拙。
所以,我的问题如下:
- 我是否需要 program.cs 末尾的“await host.RunAsync()”行?我不知道如何摆脱它,因为如果它不存在,服务就会死掉。
- 我的 ExecuteAsync 代码只是每 10 秒访问一次,什么也不做。还有什么我可以放在那里基本上说“无限期等待而不固定我的CPU”的东西吗?
- 在这种情况下设置错误处理的正确方法是什么?如果在初始化过程中出现问题(例如找不到文件),我想阻止服务启动,但如果我在构造函数中抛出错误,它似乎会继续进行,就好像什么都没发生一样。
- 有没有更好的方法来解决这个问题?我想知道如果突发事件同时发生会发生什么 - 各种事件会在它们自己的线程中处理,还是会排队等?
提前感谢您的任何建议...
解决方案
我是否需要 program.cs 末尾的“await host.RunAsync()”行?我不知道如何摆脱它,因为如果它不存在,服务就会死掉。
是的。主机是创建和启动后台服务的对象。您仍然需要运行主机。
我的 ExecuteAsync 代码只是每 10 秒访问一次,什么也不做。还有什么我可以放在那里基本上说“无限期等待而不固定我的CPU”的东西吗?
如果这对您有用,您可以随意忽略ExecuteAsync
:
protected override Task ExecuteAsync(CancellationToken) => Task.CompletedTask;
这是考虑基于事件的后台服务的一种方式:构造函数启动它并Dispose
停止它,然后ExecuteAsync
被忽略。
另一种观点是拥有一个最小的构造函数(通常被认为是好的设计),并ExecuteAsync
成为它的“主循环”。即,它在启动时ExecuteAsync
启动,并在退出之前清理ExecuteAsync
。在这种情况下,“无限”延迟是一种正常的方式,在请求关闭之前什么都不做(通过CancellationToken
)。
在这种情况下设置错误处理的正确方法是什么?如果在初始化过程中出现问题(例如找不到文件),我想阻止服务启动,但如果我在构造函数中抛出错误,它似乎会继续进行,就好像什么都没发生一样。
你确定吗?从构造函数中抛出异常应该会阻止主机获取其托管服务列表。
有没有更好的方法来解决这个问题?我想知道如果突发事件同时发生会发生什么 - 各种事件会在它们自己的线程中处理,还是会排队等?
这完全取决于EventLog
. 我相当确定每个事件都会出现在线程池线程中。