IHostedService的优雅关闭

5

我正在尝试使用.NET Core开发一个简单的API,允许异步处理请求。

  1. 请求发送到控制器
  2. 在后台服务(IHostedService)上预定工作
  3. 控制器返回202
  4. 后台服务执行长时间运行操作

由于应用程序将在IIS上运行,在控制器返回响应后,可能会发生池回收。我想实现一种方式,使应用程序能够进行平稳关闭 - 完成当前正在执行的任务,但不接受任何新任务。

我正在努力让平稳关闭顺利结束。出于测试目的,我简化了StopAsync()方法,现在它只包含一个20秒的异步等待:

public async Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Hosted service is stopping...");

        try
        {
            // Signal cancellation to the executing method
            _stoppingTokenSource.Cancel();

            _logger.LogInformation("Delay by 20s");
            await Task.Delay(20000, cancellationToken);
            _logger.LogInformation("Delay over");
        }
        finally
        {
            // Await all pending download requests
            await Task.WhenAny(Task.WhenAll(_pendingTasks), Task.Delay(Timeout.Infinite, cancellationToken));

            _logger.LogInformation("Hosted service has been stopped successfully.");
        }
    }

日志仅显示以下内容:
  • 托管服务正在停止...
  • 延迟20秒
查看EventViewer,我可以看到有两个事件在关机时被触发,并且它们之间恰好相隔10秒:
  • 发送关机HTTP消息到进程'11104'并接收http状态'202'。
  • 无法优雅地关闭进程'11104'。
我已经尝试过:

Program.cs上设置关机超时。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseShutdownTimeout(TimeSpan.FromMinutes(1));

检查应用程序池的高级设置:

enter image description here

如果您已经做过类似的事情,请告诉我是否有做错什么/指导我正确的方向?

谢谢!

编辑

在调试应用程序时,使用CTRL + C关闭会产生预期的行为。


你找到这个问题的答案了吗? - pcdev
@pcdev,我找到了一个解决方案,尽管它需要修改Web配置文件。我已经发布了它作为答案。 - Grabofus
2个回答

8

经过再次审视问题,我找到了解决方案。

似乎IIS中的ASP.NET Core模块有一个单独的关闭时间限制,可以在web.config文件中进行配置,如下:

    <configuration>
      <system.webServer>
        <handlers>
          <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
        </handlers>
        <aspNetCore
            shutdownTimeLimit="30"    - set timeout here in seconds
            processPath="dotnet"
            arguments=".\GracefulShutdown.dll"
            stdoutLogEnabled="false"
            stdoutLogFile=".\logs\stdout" />
      </system.webServer>
    </configuration>

参考设置:https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-2.1

理想情况下,这应该通过代码实现,这仍然是我正在努力解决的问题。如果你找到一种方法,请在评论中分享。


非常有用的信息,谢谢!我们正在尝试做与您概述的完全相同的事情,因此这个答案会很方便。 - pcdev
很高兴听到这个消息。我认为您仍然应该在启动时保留UseShutdownTimeout,因为它将在超时期结束后触发StopAsync方法中的令牌取消。也许将其设置为比ASP.NET Core模块实际超时时间稍短一些会更好,以避免竞争条件。 - Grabofus
不错!有没有办法将此选项移动到appsettings.json文件中? - AsValeO
1
据我所知,@AsValeO,好像不行。我认为 ASP.NET Core 模块在启动应用程序之前就已经启动了,因此我不认为 appsettings.json 可以更改其内容。如果您找到了方法,请及时更新我们! - Grabofus

2
在Configure(IApplicationBuilder app, IHostEnvironment env)函数中,注册到IHostApplicationLifetime的ApplicationStopping事件(当应用程序主机正在执行优雅关闭时触发。关闭将会阻塞直到此事件完成)。
  var applicationLifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
  applicationLifetime.ApplicationStopping.Register(OnShutdown);

私有空返回类型 OnShutdown() { 在关机前写下你想要的代码,你可以通过 Thread.Sleep() 延迟关机。 }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接