如果 IHostedService.StopAsync 方法的实现不响应取消请求会怎样?

What happens if the implementation of IHostedService.StopAsync method does not respond to cancellation requests?

我们正在实现一个.NET core 3.1 worker 服务,我们需要注册几个托管服务来实现所需的业务。

This documentation explains that the StopAsync method of the IHostedService 接口应该能够监听在默认 5 秒超时后发出的取消请求。

根据我的理解,StopAsync方法是宿主在自己关机的过程中调用的。

在我们的一个托管服务中,我们需要在托管服务停止过程中调用异步方法;不幸的是,这个方法支持取消,所以我们没有一个简单的方法来正确地实现StopAsync方法。

这是我们的场景:

public sealed class MyHostedService : IHostedService 
{
     public async Task StopAsync(CancellationToken token)
     {
          _logger.LogInformation("Stopping the service....");

           await DoSomethingAsync(); // this method does not support cancellation, so I can't pass the cancellation token here

          _logger.LogInformation("Service was stopped");
     }
}

在这种情况下主机级别会发生什么?

这个实现在任何方面都是“危险的”吗?

如果需要很长时间才能完成 StopAsync 的执行,这是否会阻止主机关闭过程?

这个问题最简单的解决方案是通过从 BackgroundService 基础 class 派生来实现 class MyHostedService,但我想完全理解可能的影响主机级别的长 运行 StopAsync 实现。

根据 generic and web hosts shutdown timeout 文档,主机将不正常地停止相应的服务:

If the timeout period expires before all of the hosted services stop, any remaining active services are stopped when the app shuts down. The services stop even if they haven't finished processing. If services require additional time to stop, increase the timeout.

这么长 运行 StopAsync 应该不会阻止主机继续关机。唯一可能发生的坏事是您的 DoSomethingAsync 将在某些无效状态下被打断,如果可能的话。

托管服务的停止顺序与它们启动的顺序相反(这也是您将它们添加到 ServiceCollection 的顺序)。您可以通过指定 HostOptions.ShutdownTimeout 来延长关闭超时,否则默认为五秒:

services.Configure<HostOptions>(s => s.ShutdownTimeout = TimeSpan.FromMinutes(5));

Host 停止其托管服务时,它会循环执行此操作,运行 以相反的顺序处理服务。一旦为特定服务调用 StopAsyncthe host will wait for StopAsync to return。因此,如果该服务不遵守取消,主机将需要一段时间才能关闭。

在每个服务上调用 StopAsync 之前,主机还在该循环中调用 ThrowIfCancellationRequested()。这意味着如果在 first 服务停止时超时已过,它将完成完成*但在 next 服务的 StopAsync 被调用会抛出异常。这将导致关闭进程中止,并导致进程异常退出。

如果在下一个瞬间超时(在循环之后紧接着另一个 ThrowIfCancellationRequested),所有服务停止后也可能发生这种中止。任何绑定到 Host/Application 生命周期的内容都将被跳过。

这个故事的寓意是确保您的服务可以快速停止,不要求您的服务需要正常终止,或者增加超时.您还可以确保以这样的方式将服务添加到集合中,即首先停止更重要或更快的服务(通过最后添加它们)并且您的长 运行 服务最后停止(通过添加它 第一个).

* 假设服务不遵守取消