我正在编写一个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在失败时重新启动服务。
我在这里找到了答案。
要正确允许服务重新启动,您可以调用 使用非零退出代码退出环境。然而答案仍然不太令人满意。即使发生了错误,我还是愿意关闭尽可能多的服务并调用尽可能多的 Dispose 方法。我希望 IHost.Run() 干净地停止并从 main 返回非零。 这似乎不可能。
Environment.Exit(1)
需要从托管服务调用,不幸的是,该服务不能完全退出。
ExecuteAsync
预热时立即允许主机继续,您可以在需要用
await Task.Yield
保护的任何内容之前放置
try
,然后使用 try 避免向主机抛出异常。捕获后台服务中的所有错误并使用它们来设置
Environment.ExitCode
,这就是您可以向主机标记关键服务失败的方法。
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;
}
}