当 Kestrel 收到请求时无法停止 asp.net 核心 Windows 服务

Unable to stop asp.net core Windows Service when Kestrel receives requests

我们在 .net 4.5.1 上使用 asp.net 核心 运行 作为 Windows 服务。 Kestrel 用作 Web 服务器(Kestrel 版本为 1.1.2)。 它是根据 Host an ASP.NET Core app in a Windows Service 配置的。我们还在 OWIN 上使用 SignalR 运行。

当两个事件同时发生时会出现问题:Windows服务正在停止并且 Krestel 收到了一些新的 Web 请求。如果满足这些条件,则 Windows 服务停止响应并挂起。

Windows could not stop the Topshelf.Host service on Local Computer. Error 1061: The service cannot accept control messages at this time.

配置代码:

var port = Global.Settings.Port;
var hostBuilder = new WebHostBuilder()                  
                .UseKestrel()                   
                .UseStartup<Startup>()
                .UseUrls("http://+:" + port);

if (runAsService)
 {                    
       hostBuilder.UseContentRoot(directoryPath);
       Directory.SetCurrentDirectory(directoryPath);
 }
 else
 {                    
     hostBuilder.UseContentRoot(Directory.GetCurrentDirectory());
 }

var host = hostBuilder.Build();
if (runAsService)
{                 
    host.RunAsCustomService();
}
else
{                    
      host.Run();
}

public static class WebHostServiceExtensions
{
    public static void RunAsCustomService(this IWebHost host)
    {
        var webHostService = new CustomWebHostService(host);
        ServiceBase.Run(webHostService);
    }
}
internal class CustomWebHostService : WebHostService
{
    public CustomWebHostService(IWebHost host) : base(host)
    {
    }

//...
}

进一步调查显示WebHostService.cs.

中处理 _host 时出现未处理的异常
protected sealed override void OnStop()
{
    _stopRequestedByWindows = true;
    OnStopping();
    _host?.Dispose();
    OnStopped();
}

如下所述,它引发异常“Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException:错误 -4082 EBUSY 资源繁忙或已锁定”

问题是:

有没有办法避免这种错误并防止Windows服务挂起?例如。在 _host 处理或阻止 OnStopping 事件中的新传入连接之前停止 Krestel。

堆栈跟踪:

Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -4082 EBUSY resource busy or locked
in Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv.loop_close(UvLoopHandle handle) in Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvLoopHandle.ReleaseHandle() in System.Runtime.InteropServices.SafeHandle.InternalDispose() in System.Runtime.InteropServices.SafeHandle.Dispose(Boolean disposing)
in System.Runtime.InteropServices.SafeHandle.Dispose() in Microsoft.AspNetCore.Server.Kestrel.Internal.KestrelThread.ThreadStart(Object parameter)

in Microsoft.AspNetCore.Server.Kestrel.Internal.KestrelThread.d__51.MoveNext() in System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken) in Microsoft.AspNetCore.Server.Kestrel.Internal.KestrelEngine.Dispose()
in Microsoft.AspNetCore.Server.Kestrel.KestrelServer.Dispose() in Microsoft.Extensions.DependencyInjection.ServiceProvider.Dispose()
in Microsoft.AspNetCore.Hosting.Internal.WebHost.Dispose()

Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -4082 EBUSY resource busy or locked in Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv.loop_close(UvLoopHandle handle) in Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvLoopHandle.ReleaseHandle() in System.Runtime.InteropServices.SafeHandle.InternalDispose() in System.Runtime.InteropServices.SafeHandle.Dispose(Boolean disposing)
in System.Runtime.InteropServices.SafeHandle.Dispose() in Microsoft.AspNetCore.Server.Kestrel.Internal.KestrelThread.ThreadStart(Object parameter)

в Microsoft.AspNetCore.Server.Kestrel.Internal.KestrelThread.d__51.MoveNext()

Kestrel 2.0 版本中 KestrelServer.Dispose() will be fixed 引发的 EBUSY 异常。我认为您的服务挂起的原因是 _host?.Dispose() 正在抛出,这会阻止 OnStopped();OnStop() 方法中被调用。

与此同时,吞下异常以确保 OnStopped(); 始终被调用应该允许停止服务。如果要重新启动服务器,请确保构建一个新的 WebHost。