SCM 未重启 BackgroundService

BackgroundService not restarted by SCM

目标:

运行 Windows 上的 .net 6.0 BackgroundService,当发生未处理的异常时自动重新启动。

最小示例:

Program.cs

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
    })
    .UseWindowsService(options =>
    {
        options.ServiceName = "ASampleService";
    })
    .Build();
await host.RunAsync();

Worker.cs

namespace SampleWindowsService
{
    public class Worker : BackgroundService
    {
        [...]

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
                throw new Exception();
            }
        }
    }
}

sc 故障配置

sc failure ASampleService reset=0 actions= restart/5000

预期行为

服务应在启动 10 秒后崩溃,然后在崩溃 5 秒后重新启动。它应该无限期地这样做。系统事件日志应包含 ASampleService 崩溃并将在 5000 毫秒内由服务控制管理器重新启动的日志行。

实际行为

服务崩溃并在应用程序事件日志中生成日志行“服务已成功停止”并且永远不会重新启动。

问题

我猜“已成功停止”记录行是问题的核心。根据 this article :

“A service is considered failed when it terminates without reporting a status of SERVICE_STOPPED to the service controller.”

但是,我还没有找到阻止服务“成功停止”的方法,即使覆盖 BackgroundService 的 StopAsync 函数或 IHostApplicationLifecycle 的 OnStop 并在那里抛出异常,也没有阻止该日志行。

我一直在深入研究源代码以了解正在发生的事情,我开始认为我可能需要使用较低级别的抽象来使其工作。但是,如果这里有人知道如何让它发挥作用,我很想听听您的方法。

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                // simulate some work here
                await Task.Delay(TimeSpan.FromSeconds(5));

                // something went wrong
                throw new Exception();
            }
        }
        catch
        {
            // prevent "Stopped successfully"
            Environment.Exit(1);
        }
    }

.net 文档中的文章也将更新。您可能还想将退出代码设置为异常的 HResult。

使用此代码,scm 将重新启动服务,系统日志将显示正确的消息:

The SampleService service terminated unexpectedly. It has done this 1 time(s). The following corrective action will be taken in 5000 milliseconds: Restart the service.