控制docker中托管的.NET Core控制台应用程序的生命周期

Jay*_*Jay 11 c# linux servicestack docker .net-core

免责声明 - 这与Docker容器即使在.net核心控制台应用程序中使用Console.ReadLine()立即退出几乎是同一个问题- 但我不认为这个问题的接受答案是令人满意的.

我想要实现的目标
我正在构建一个控制台应用程序(它是一个使用ServiceStack的HTTP服务),它是用.NET内核构建的(dnxcore50 - 这是一个控制台应用程序,而不是ASP.NET应用程序).我在Linux机器上的docker容器中运行此应用程序.我已经完成了,HTTP服务正常工作.

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

Repo
代码下面的代码是用于创建测试.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; }
}
Run Code Online (Sandbox Code Playgroud)

解决此问题的旧方法
因此以前使用Mono托管(Mono有严重的性能问题 - 因此切换到.NET核心) - 修复此行为的方法是使用Mono.Posixlisten来查找这样的kill信号:

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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在 - 我明白这对.NET Core不起作用(显然是因为Mono.Posix用于Mono!)

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

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

代码重构是Mythz建议的一部分

 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();
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

最终解决方案

对于后代 - 我已经使用的解决方案是可以在这里找到的代码(感谢神话澄清):https://github.com/NetCoreApps/Hello/blob/master/src/SelfHost/Program.cs

相关代码的回购:

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);
        });
    }
Run Code Online (Sandbox Code Playgroud)

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

myt*_*thz 10

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

AppHostSelfBase只是.NET Core托管API包装,但是调用非阻塞IWebHost.Start().要获得的行为IWebHost.Run(),你应该能够重复使用的相同的方法ManualResetEventSlimConsole.CancelKeyPressWebHost.Run()的实现使用,但亲自它只是更容易使用.NET Core的托管API和调用Run(),只是注册ServiceStack APPHOST作为. NET核心模块.