1

我对 .NET Core 和开发 linux 守护程序都是全新的。我遇到了几个类似的问题,例如在 Linux 上优雅地杀死一个 .NET Core 守护程序在 .NET Core 2.1 中使用 Generic Host 优雅地关闭,但它们并没有解决我的问题。

我使用托管服务构建了一个非常简单的控制台应用程序作为测试。我希望它作为守护进程运行,但我无法正确关闭它。当它在 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>or之后systemctl stop <service>,会执行StopAsyncandDispose方法,但不会执行其他操作:不是 inMain之后的块,await也不是finally块。

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

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

4

2 回答 2

1

这个答案对于 dotnet core 3.1 是正确的,但应该是一样的。

host.RunConsoleAsync() 等待 Sigterm 或 ctrl + C。

切换到 host.Start() 并在 IHostedServices 完成时停止程序。

我不认为这条线目前受到打击:

System.IO.File.WriteAllText("/path-to-app/_main.txt", "Line 2");
于 2020-03-11T18:47:56.007 回答
0

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

它显示为IHostedService用于HostBuilder控制SIGTERM. 一旦Task被标记为已完成,它就确定服务已正常关闭。通过将System.IO.File.WriteAllText("/path-to-app/_main.txt", "Line 2");finally 块中的和代码移动到服务范围内,这可以得到修复。下面提供了修改后的代码。

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");
        }
    }
}

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

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

于 2019-04-24T13:17:38.663 回答