【问题标题】:Control lifetime of .NET Core console application hosted in docker控制托管在 docker 中的 .NET Core 控制台应用程序的生命周期
【发布时间】:2016-11-09 22:53:37
【问题描述】:

免责声明 - 这与 docker container exits immediately even with Console.ReadLine() in a .net core console application 几乎是同一个问题 - 但我认为这个问题的公认答案并不令人满意。

我正在努力实现的目标
我正在构建一个控制台应用程序(它是使用 ServiceStack 的 HTTP 服务),它是用 .NET 核心(dnxcore50 - 这是一个控制台应用程序,而不是 ASP.NET 应用程序)构建的。我在 Linux 机器上的 docker 容器中运行这个应用程序。我已经这样做了,并且 HTTP 服务可以正常工作。

我的问题
话虽如此,“我的服务有效” - 确实如此,但在 docker 容器中托管服务存在问题。我在启动 HTTP 侦听器后使用Console.ReadLine(),但此代码不会在 docker 容器内阻塞,容器将在启动后立即退出。我可以在“交互”模式下启动 docker 容器,服务将坐在那里监听,直到我终止交互会话,然后容器将退出。

回购代码
下面的代码是用于创建我的测试 .NET 核心服务堆栈控制台应用程序的完整代码清单。

public class Program
{
    public static void Main(string[] args)
    {
        new AppHost().Init().Start("http://*:8088/");
        Console.WriteLine("listening on port 8088");
        Console.ReadLine();

    }
}

public class AppHost : AppSelfHostBase
{
    // Initializes your AppHost Instance, with the Service Name and assembly containing the Services
    public AppHost() : base("My Test Service", typeof(MyTestService).GetAssembly()) { }

    // Configure your AppHost with the necessary configuration and dependencies your App needs
    public override void Configure(Container container)
    {

    }
}

public class MyTestService: Service
{
    public TestResponse Any(TestRequest request)
    {
        string message = string.Format("Hello {0}", request.Name);
        Console.WriteLine(message);
        return new TestResponse {Message = message};
    }

}

[Api("Test method")]
[Route("/test/{Name}", "GET", Summary = "Get Message", Notes = "Gets a message incorporating the passed in name")]
public class TestRequest : IReturn<TestResponse>
{
    [ApiMember(Name = "Name", Description = "Your Name", ParameterType = "path", DataType = "string")]
    public string Name { get; set; }
}

public class TestResponse 
{
    [ApiMember(Name = "Message", Description = "A Message", ParameterType = "path", DataType = "string")]
    public string Message { get; set; }
}

解决这个问题的老办法
因此,以前使用 Mono 托管(Mono 存在严重的性能问题 - 因此切换到 .NET 核心) - 修复此行为的方法是使用 Mono.Posix 监听如下终止信号:

using Mono.Unix;
using Mono.Unix.Native;

...

static void Main(string[] args)
    {
        //Start your service here...

        // check if we're running on mono
        if (Type.GetType("Mono.Runtime") != null)
        {
            // on mono, processes will usually run as daemons - this allows you to listen
            // for termination signals (ctrl+c, shutdown, etc) and finalize correctly
            UnixSignal.WaitAny(new[] {
                new UnixSignal(Signum.SIGINT),
                new UnixSignal(Signum.SIGTERM),
                new UnixSignal(Signum.SIGQUIT),
                new UnixSignal(Signum.SIGHUP)
            });
        }
        else
        {
            Console.ReadLine();
        }
    }

现在 - 我知道这不适用于 .NET Core(显然是因为 Mono.Posix 是用于 Mono!)

相关文章(本文顶部)中概述的解决方案对我没有用 - 在生产环境中,我不能指望通过确保 docker 容器具有可用的交互式会话来保持其活动状态,这将保持Console.ReadLine 工作,因为那里有一个 STD-IN 流......

在托管 .NET Core 应用程序时,是否有另一种方法可以让我的 docker 容器保持活动状态(在调用 docker run 时使用 -d(分离)选项)?

将代码重构作为神话建议的一部分

 public static void Main(string[] args)
    {
        Run(new AppHost().Init(), "http://*:8088/");
    }

    public static void Run(ServiceStackHost host, params string[] uris)
    {
        AppSelfHostBase appSelfHostBase = (AppSelfHostBase)host;

        using (IWebHost webHost = appSelfHostBase.ConfigureHost(new WebHostBuilder(), uris).Build())
        {
            ManualResetEventSlim done = new ManualResetEventSlim(false);
            using (CancellationTokenSource cts = new CancellationTokenSource())
            {
                Action shutdown = () =>
                {
                    if (!cts.IsCancellationRequested)
                    {
                        Console.WriteLine("Application is shutting down...");
                        cts.Cancel();
                    }

                    done.Wait();
                };

                Console.CancelKeyPress += (sender, eventArgs) =>
                {
                    shutdown();
                    // Don't terminate the process immediately, wait for the Main thread to exit gracefully.
                    eventArgs.Cancel = true;
                };

                Console.WriteLine("Application started. Press Ctrl+C to shut down.");
                webHost.Run(cts.Token);
                done.Set();
            }
        }
    }

最终解决方案!

对于后代 - 我采用的解决方案是可以在此处找到的代码(感谢 Myths 的澄清):https://github.com/NetCoreApps/Hello/blob/master/src/SelfHost/Program.cs

相关代码的repo:

public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .UseUrls("http://*:8088/")
            .Build();

        host.Run();
    }
}

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // app.UseStaticFiles();

        app.UseServiceStack(new AppHost());

        app.Run(context =>
        {
            context.Response.Redirect("/metadata");
            return Task.FromResult(0);
        });
    }

在 NuGet 中,我安装了 Microsoft.NETCore.App、ServiceStack.Core 和 ServiceStack.Kestrel。

【问题讨论】:

    标签: c# linux docker servicestack .net-core


    【解决方案1】:

    如果您要在 Docker 中托管 .NET Core 应用程序,我建议您按照正常的 .NET Core Hosting API 调用 IWebHost.Run() 来阻止主线程并保持控制台应用程序处于活动状态。

    AppHostSelfBase 只是一个wrapper around .NET Core's hosting API,但会调用非阻塞IWebHost.Start()。要获得IWebHost.Run() 的行为,您应该能够重用与WebHost.Run()'s implementation uses 相同的ManualResetEventSlimConsole.CancelKeyPress 方法,但就个人而言,使用.NET Core 的托管API 并调用Run() 和@ 987654324@.

    【讨论】:

    • 我已经尝试使用您所建议的(我认为)代码 - 它似乎在 Windows 和我的 Linux Docker 中都可以工作......更新是针对我原来的问题 -在帖子的底部。您认为解决方案如何?我的理解正确吗?
    • @Jay 如果它有效我确信它很好,但为什么不使用 .NET Core 推荐的托管模型呢?目前尚不清楚您为什么要维护自己的实现。
    • 可能是因为我还不明白 :) 我完全是 .NET 核心的菜鸟,真的不明白这里的例子:docs.servicestack.net/releases/… 将在 Linux 中工作;即UseIISIntegration() 在 Linux 容器中做了什么,以及如何设置我想监听的端口。我非常习惯于使用 ServiceStack 创建简单的自托管控制台应用程序 - 我想要一些与以前几乎相同的东西。也许我需要多读一点 .NET 核心和托管模型。
    • @Jay .NET Core 中的所有应用程序都是自托管控制台应用程序,并且 UseIISIntegration() 在 Linux 上被忽略,因此您可以忽略/删除它。您不需要了解 .NET Core 托管模型,只需保持原样即可,它的主要作用是指向您的自定义 Startup 类并侦听您指定的 URL。查看github.com/NetCoreApps 上现有的 .NET Core 现场演示,其中包含 ServiceStack 现有现场演示的小型独立示例。
    • @Jay 每个 .NET Core 示例也在 Windows 和 Linux 上按原样运行。它们都托管在 Linux/Docker 上,并使用 AWS ECS 部署,使用每个存储库中包含的部署脚本,如本分步指南中所述:docs.servicestack.net/deploy-netcore-docker-aws-ecs
    猜你喜欢
    • 2021-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多