如何让Windows在失败时重新启动.net服务

问题描述 投票:0回答:2

我正在编写一个Windows服务。不幸的是,我被迫使用一个有错误的库。当这个错误出现时,除了重新启动进程之外,我无法通过其他方式消除它。将 main 函数中的所有代码包装在循环中是不够的。我认为我需要以某种方式卸载并重新加载外部本机库,或者让 Windows 在失败时自动重新启动服务。

我尝试按如下方式配置服务:

sc failure "MyService" reset= 30 actions= restart/5000

这有时有效。我可以看到 Windows 能够一遍又一遍地重新启动该服务。 不幸的是,这取决于服务在 C# 代码中如何崩溃,并且在某些情况下不会重新启动服务。这是我正在使用的 c# 代码:

WebApplicationBuilder builder = WebApplication.CreateBuilder(options);

builder.Services.AddHostedService(provider => new MyHostedService());

builder.Host.UseWindowsService();
WebApplication app = builder.Build();
app.Run();

我的观察如下:如果在第一次等待之前在 MyHostedService 中抛出未捕获的异常,那么

app.Run()
也会抛出异常,并且 Windows 按照我的意愿正确地重新启动服务。

但是,如果 MyHostedService 运行一段时间并等待某件事多次,然后发生异常,然后

app.Run()
就会返回,Windows 不会重新启动该服务。

这会记录到事件查看器中:

HostOptions.BackgroundServiceExceptionBehavior 配置为 StopHost。 BackgroundService 引发了未处理的异常,并且 IHost 实例正在停止。要避免此行为,请将其配置为“忽略”;但是,BackgroundService 将不会重新启动。

我尝试在

app.Run()
之后立即从 Main 抛出或返回非零值,但没有任何效果。

我还考虑过更改 HostOptions.BackgroundServiceExceptionBehavior 的值。但另一种可能性是忽略我不想要的错误。

外部库的奇怪行为只需要重新启动对我不起作用的进程。非常感谢您提前如何修改代码以使得Windows在失败时重新启动服务。

c# .net windows-services .net-6.0 application-restart
2个回答
1
投票

我在这里找到了答案。

要正确允许服务重新启动,您可以调用 使用非零退出代码退出环境。

然而答案仍然不太令人满意。即使发生了错误,我还是愿意关闭尽可能多的服务并调用尽可能多的 Dispose 方法。我希望 IHost.Run() 干净地停止并从 main 返回非零。 这似乎不可能。

Environment.Exit(1)

 需要从托管服务调用,不幸的是,该服务不能完全退出。


0
投票
有两件事:

  1. 要在

    ExecuteAsync

     预热时立即允许主机继续,您可以在需要用 
    await Task.Yield
     保护的任何内容之前放置 
    try
    ,然后使用 try 避免向主机抛出异常。捕获后台服务中的所有错误并使用它们来设置 
    Environment.ExitCode
    ,这就是您可以向主机标记关键服务失败的方法。

  2. Vista(2008)之后,服务配置中还有另一个选项可用。请参阅此处:

    使用退出代码使 Windows 服务失败

您可以通过编程方式将服务设置为在命令行退出代码时重新启动:

sc.exe failureflag "ServiceName" 1
主要

WebApplicationBuilder builder = WebApplication.CreateBuilder(options); builder.Services.AddHostedService(provider => new MyHostedService()); builder.Host.UseWindowsService(); WebApplication app = builder.Build(); await app.RunAsync();
我的托管服务

protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { // Tell the host to continue without us. await Task.Yield(); // Your code goes here catch (Exception ex) { // Use to indicate failure to Windows Service Control Manager (SCM) Environment.ExitCode = ex.HResult; } }
    
© www.soinside.com 2019 - 2024. All rights reserved.