【发布时间】: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