asp.net-core - Asp.Net Core 3 正常关机抛出 OperationCanceledException

标签 asp.net-core asp.net-core-webapi asp.net-core-3.1

我正在尝试正常终止 ASP.Net Core 3.1 服务(将在 Kubernetes 中运行)。当 Kubernetes 停止服务时,它会向应用程序发送一个 SIGTERM 事件,此时我希望在终止之前完成飞行中的请求(这可能需要几秒钟)......我想我可以在托管服务中捕获它,因为下面,因此不会立即停止。

以下工作正常,但超时为 5 秒或更长时间,我收到 OperationCanceledException。任何人都可以阐明为什么我会收到 OperationCanceledException 或如何阐明延迟 SIGTERM 事件以允许正常关闭的替代方法吗?

    public static int Main(string[] args)
    {
        var logger = NLogBuilder
            .ConfigureNLog("nlog.config")
            .GetCurrentClassLogger();
        try
        {
            CreateHostBuilder(args)
                .ConfigureServices((hostBuilderContext, services) => { services.AddHostedService<LifetimeEventsHostedService>(); })
                .Build()
                .Run();

            return 0;
        }
        catch (Exception e)
        {
            logger.Fatal(e, "Stopping due to exception");

            return -1;
        }
        finally
        {
            LogManager.Shutdown();
        }
    }

这是托管服务...

    internal class LifetimeEventsHostedService : IHostedService
    {
        private readonly Microsoft.Extensions.Logging.ILogger _logger;
        private readonly IHostApplicationLifetime _appLifetime;

        public LifetimeEventsHostedService(
            ILogger<LifetimeEventsHostedService> logger,
            IHostApplicationLifetime appLifetime)
        {
            _logger = logger;
            _appLifetime = appLifetime;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _appLifetime.ApplicationStarted.Register(OnStarted);
            _appLifetime.ApplicationStopping.Register(OnStopping);
            _appLifetime.ApplicationStopped.Register(OnStopped);

            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            return Task.CompletedTask;
        }

        private void OnStarted()
        {
            _logger.LogInformation("OnStarted has been called.");

            // Perform post-startup activities here
        }

        private void OnStopping()
        {
            _logger.LogInformation("OnStopping has been called.");
            // Perform on-stopping activities here


            // This works, but a timeout of 5 seconds or more subsequently causes an OperationCanceledException
            Thread.Sleep(5000);
        }

        private void OnStopped()
        {
            _logger.LogInformation("OnStopped has been called.");

            // Perform post-stopped activities here
        }
    }

最佳答案

我对使用 ASP.Net Core 3.1 正常关闭的替代方法持开放态度,就目前而言,我正在使用托管服务。

在 .Net Core 应用程序中,我设置了 ShutdownTimeout on the webhost , 但是,设置 ShutdownTimeout on the generic host ,确实允许我在关机前优雅地等待几秒钟(超过默认的 sigterm,即 5 秒)。 @PmanAce 的提示帮助我解决了这个问题。

因此,以下代码允许我优雅地终止。需要注意的是,LifetimeEventsHostedService 中的 Thread.Sleep 必须小于 option.ShutdownTimeout

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostBuilderContext, services) =>
            {
                services.AddHostedService<LifetimeEventsHostedService>();
                services.Configure<HostOptions>(option =>
                {
                    option.ShutdownTimeout = TimeSpan.FromSeconds(30);
                });
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseKestrel();
                webBuilder.UseStartup<Startup>();
            });
}

下面的LifetimeEventsHostedService

public class LifetimeEventsHostedService : IHostedService
{
    private readonly IHostApplicationLifetime _hostApplicationLifetime;

    public LifetimeEventsHostedService(IHostApplicationLifetime hostApplicationLifetime)
    {
        _hostApplicationLifetime = hostApplicationLifetime;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _hostApplicationLifetime.ApplicationStarted.Register(OnStarted);
        _hostApplicationLifetime.ApplicationStopping.Register(OnStopping);
        _hostApplicationLifetime.ApplicationStopped.Register(OnStopped);

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    private void OnStopped()
    {
        Console.WriteLine("OnStopped");
    }

    private void OnStopping()
    {
        Console.WriteLine("OnStopping");
        Console.WriteLine(DateTime.Now.ToLongTimeString());

        Thread.Sleep(15000);
        Console.WriteLine("Sleep finished");
        Console.WriteLine(DateTime.Now.ToLongTimeString());
    }

    private void OnStarted()
    {
        Console.WriteLine("OnStarted");
    }
}

关于asp.net-core - Asp.Net Core 3 正常关机抛出 OperationCanceledException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60228641/

相关文章:

c# - 在 Web Api 中的 Asp Core 2.2 中创建自定义返回错误验证

c# - 输入字符串的格式不正确 - 使用了错误的上下文

unit-testing - 使用服务定位器时如何为 ActionFilter 编写单元测试

c# - 验证 POST 请求的输入?

asp.net-core - 无法解析类型 'Microsoft.ApplicationInsights.TelemetryClient' 的服务

identityserver3 - 如何在 asp.net core 2.0 web api 中使用 identityserver3 来验证来自 Identityserver3 服务器的 token

c# - ASP.NET Core 1 (RTM) 中的自定义 DateTime 模型绑定(bind)器

entity-framework - 如何将用户管理器包含到 ASP.NET Core 3.1 的另一个应用程序中

json - ASP.NET Core 3 - Serilog 如何在 appsettings.json 文件中配置 Serilog.Sinks.Map?

asp.net-core - .NET CORE WEB API 接受整数列表作为 HTTP GET API 中的输入参数