正常关闭 .NET Core 2 linux 守护程序中的通用主机



我对.NET Core和开发linux守护进程都是全新的。我经历过几个类似的问题,比如优雅地杀死在 Linux 上运行的 .NET Core 守护进程或在 .NET Core 2.1 中使用通用主机优雅关闭,但它们并没有解决我的问题。

我构建了一个非常简单的控制台应用程序作为使用托管服务的测试。我希望它作为守护程序运行,但我在正确关闭它时遇到问题。当它在Windows和Linux中从控制台运行时,一切正常。

public static async Task Main(string[] args)
{
    try
    {
        Console.WriteLine("Starting");
        var host = new HostBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<DaemonService>();
            });
        System.IO.File.WriteAllText("/path-to-app/_main.txt", "Line 1");
        await host.RunConsoleAsync();
        System.IO.File.WriteAllText("/path-to-app/_main.txt", "Line 2");
    }
    finally
    {
        System.IO.File.WriteAllText("/path-to-app/_main-finally.txt", "Line 1");
    }
}
public class DaemonService : IHostedService, IDisposable
{
    public Task StartAsync(CancellationToken cancellationToken)
    {
        System.IO.File.WriteAllText("/path-to-app/_Start.txt", "Line 1");
        return Task.CompletedTask;
    }
    public Task StopAsync(CancellationToken cancellationToken)
    {
        System.IO.File.WriteAllText("/path-to-app/_Stop.txt", "Line 1");
        return Task.CompletedTask;
    }
    public void Dispose()
    {
        System.IO.File.WriteAllText("/path-to-app/_Dispose.txt", "Line 1");
    }
}

如果我从控制台运行应用程序,一切都按预期工作。但是,当它作为守护程序运行时,在执行kill <pid>systemctl stop <service>之后,将执行StopAsyncDispose方法,但不执行其他任何方法:不是await之后的in Main,也不是finally块。

注意:我没有使用 ASP.NET Core中的任何内容。AFAIK 我正在做的事情没有必要。

我做错了什么吗?这是预期的行为吗?

这个答案对于dotnet core 3.1是正确的,但它应该是相同的。

主机。RunConsoleAsync(( 等待 Sigterm 或 ctrl + C。

切换到主机。Start(( 和程序在 IHostedServices 完成时停止。

我认为这条线目前没有被击中:

System.IO.File.WriteAllText("/path-to-app/_main.txt", "Line 2");

总结最初问题下方的对话。

看来HostBuilder中使用的IHostedService是控制SIGTERM的东西。将Task标记为已完成后,它将确定服务已正常关闭。通过将 finally 块中的System.IO.File.WriteAllText("/path-to-app/_main.txt", "Line 2");和代码移动到服务范围内,可以解决此问题。下面提供了修改后的代码。

public static async Task Main(string[] args)
{
    Console.WriteLine("Starting");
    var host = new HostBuilder()
        .ConfigureServices((hostContext, services) =>
        {
           services.AddHostedService<DaemonService>();
        });
    System.IO.File.WriteAllText("/path-to-app/_main.txt", "Line 1");
    await host.RunConsoleAsync();
}
public class DaemonService : IHostedService, IDisposable
{
    public Task StartAsync(CancellationToken cancellationToken)
    {
        System.IO.File.WriteAllText("/path-to-app/_Start.txt", "Line 1");
        return Task.CompletedTask;
    }
    public Task StopAsync(CancellationToken cancellationToken)
    {
            return Task.CompletedTask;
    }
    public void Dispose()
    {
        try
        {
            System.IO.File.WriteAllText("/path-to-app/_Dispose.txt", "Line 1");
            System.IO.File.WriteAllText("/path-to-app/_Stop.txt", "Line 1");
        }
        finally
        {
            System.IO.File.WriteAllText("/path-to-app/_main-finally.txt", "Line 1");
        }
    }
}

由于这是作为服务运行的,我们得出的结论是,在该范围内包含服务本身的最终确定实际上是有意义的,类似于 Core 应用程序 ASP.NET 仅在 Program.cs 文件中提供服务并允许服务本身维护其依赖项的方式。

我的建议是在服务中包含尽可能多的内容,并让Main方法启动它。

相关内容

最新更新