【半译】扩展shutdown超时设置以保证IHostedService正常关闭

我最近发现一个问题,当应用程序关闭时,我们的应用程序没有正确执行在IHostedService中的StopAsync方法。经过反复验证发现,这是由于某些服务对关闭信号做出响应所需的时间太长导致的。在这篇文章中,我将展示出现这个问题的一个示例,并且会讨论它为什么会发生以及如何避免这种情况出现。

作者:依乐祝

首发地址:https://www.cnblogs.com/yilezhu/p/12952977.html

英文地址:https://andrewlock.net/extending-the-shutdown-timeout-setting-to-ensure-graceful-ihostedservice-shutdown/

使用IHostedService运行后台服务

ASP.NET Core 2.0引入了IHostedService用于运行后台任务的界面。该接口包含两种方法:

public interface IHostedService { Task StartAsync(CancellationToken cancellationToken); Task StopAsync(CancellationToken cancellationToken); }

StartAsync在应用程序启动时被调用。在ASP.NET核心2.X发生这种情况在应用程序启动处理请求,而在ASP.NET核心3.x中托管服务开始在应用程序启动处理请求。

StopAsync当应用程序收到shutdown(SIGTERM)信号时(例如,您CTRL+C在控制台窗口中按入,或者应用程序被主机系统停止时),将调用。这样,您就可以关闭所有打开的连接,处置资源,并通常根据需要清理类。

实际上,实现此接口实际上有一些微妙之处,这意味着您通常希望派生。

如果您想了解更多,Steve Gordon会开设有关Pluralsight的课程“ 构建ASP.NET Core托管服务和.NET Core Worker Services ”。

关闭IHostedService实施的问题

我最近看到的问题是OperationCanceledException在应用程序关闭时引发的问题:

Unhandled exception. System.OperationCanceledException: The operation was canceled. at System.Threading.CancellationToken.ThrowOperationCanceledException() at Microsoft.Extensions.Hosting.Internal.Host.StopAsync(CancellationToken cancellationToken)

我将这个问题的根源追溯到一个特定的IHostedService实现。我们将IHostedServices作为每个Kafka消费者的主机。具体操作并不重要-关键在于关闭IHostedService相对较慢:取消订阅可能需要几秒钟。

问题的一部分是Kafka库(和基础librdkafka库)使用同步阻塞Consume调用而不是异步可取消调用的方式。解决这个问题的方法不是很好。

理解此问题的简便方法是一个示例。

演示问题

解决此问题的最简单方法是创建一个包含两个IHostedService实现的应用程序:

NormalHostedService 在启动和关闭时记录日志,然后立即返回。

SlowHostedService 记录启动和停止的时间,但要花10秒才能完成关闭

这两个类的实现如下所示。的NormalHostedService很简单:

public class NormalHostedService : IHostedService { readonly ILogger<NormalHostedService> _logger; public NormalHostedService(ILogger<NormalHostedService> logger) { _logger = logger; } public Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("NormalHostedService started"); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("NormalHostedService stopped"); return Task.CompletedTask; } }

在SlowHostedService几乎是相同的,但它有一个Task.Delay是需要10秒,以模拟一个缓慢的关机

public class SlowHostedService : IHostedService { readonly ILogger<SlowHostedService> _logger; public SlowHostedService(ILogger<SlowHostedService> logger) { _logger = logger; } public Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("SlowHostedService started"); return Task.CompletedTask; } public async Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("SlowHostedService stopping..."); await Task.Delay(10_000); _logger.LogInformation("SlowHostedService stopped"); } }

的IHostedService就是我曾在实践中只用了1秒关机,但我们有很多人,所以整体效果是一样的上面!

该服务中注册的顺序ConfigureServices是非常重要的在这种情况下-来证明这个问题,我们需要SlowHostedService被关闭第一。服务以相反的顺序关闭,这意味着我们需要最后注册它:

public void ConfigureServices(IServiceCollection services) { services.AddHostedService<NormalHostedService>(); services.AddHostedService<SlowHostedService>(); }

当我们运行该应用程序时,您将像往常一样看到启动日志:

info: ExampleApp.NormalHostedService[0] NormalHostedService started info: ExampleApp.SlowHostedService[0] SlowHostedService started ... info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down.

但是,如果按CTRL+C关闭该应用程序,则会出现问题。在SlowHostedService完成关闭,但随后一个OperationCanceledException被抛出:

info: Microsoft.Hosting.Lifetime[0] Application is shutting down... info: ExampleApp.SlowHostedService[0] SlowHostedService stopping... info: ExampleApp.SlowHostedService[0] SlowHostedService stopped Unhandled exception. System.OperationCanceledException: The operation was canceled. at System.Threading.CancellationToken.ThrowOperationCanceledException() at Microsoft.Extensions.Hosting.Internal.Host.StopAsync(CancellationToken cancellationToken) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.WaitForShutdownAsync(IHost host, CancellationToken token) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host) at ExampleApp.Program.Main(String[] args) in C:\repos\andrewlock\blog-examples\SlowShutdown\Program.cs:line 16

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppjpy.html