我可以编写一个长期运行的CLI应用程序并运行它,但我认为它不符合标准兼容的Linux守护程序的所有期望(响应SIGTERM,由System V init进程启动,忽略终端I / O信号,etc.)
大多数生态系统都有一些最佳实践方法,例如,在python中,您可以使用https://pypi.python.org/pypi/python-daemon/
是否有一些关于如何使用.Net Core执行此操作的文档?
答案 0 :(得分:24)
我玩弄了一个类似于.net核心web主机如何在控制台应用程序中等待关闭的想法。我正在GitHub上查看它,并能够提取他们如何执行Run
public static class ConsoleHost {
/// <summary>
/// Block the calling thread until shutdown is triggered via Ctrl+C or SIGTERM.
/// </summary>
public static void WaitForShutdown() {
WaitForShutdownAsync().GetAwaiter().GetResult();
}
/// <summary>
/// Runs an application and block the calling thread until host shutdown.
/// </summary>
/// <param name="host">The <see cref="IWebHost"/> to run.</param>
public static void Wait() {
WaitAsync().GetAwaiter().GetResult();
}
/// <summary>
/// Runs an application and returns a Task that only completes when the token is triggered or shutdown is triggered.
/// </summary>
/// <param name="host">The <see cref="IConsoleHost"/> to run.</param>
/// <param name="token">The token to trigger shutdown.</param>
public static async Task WaitAsync(CancellationToken token = default(CancellationToken)) {
//Wait for the token shutdown if it can be cancelled
if (token.CanBeCanceled) {
await WaitAsync(token, shutdownMessage: null);
return;
}
//If token cannot be cancelled, attach Ctrl+C and SIGTERN shutdown
var done = new ManualResetEventSlim(false);
using (var cts = new CancellationTokenSource()) {
AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: "Application is shutting down...");
await WaitAsync(cts.Token, "Application running. Press Ctrl+C to shut down.");
done.Set();
}
}
/// <summary>
/// Returns a Task that completes when shutdown is triggered via the given token, Ctrl+C or SIGTERM.
/// </summary>
/// <param name="token">The token to trigger shutdown.</param>
public static async Task WaitForShutdownAsync(CancellationToken token = default (CancellationToken)) {
var done = new ManualResetEventSlim(false);
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(token)) {
AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: string.Empty);
await WaitForTokenShutdownAsync(cts.Token);
done.Set();
}
}
private static async Task WaitAsync(CancellationToken token, string shutdownMessage) {
if (!string.IsNullOrEmpty(shutdownMessage)) {
Console.WriteLine(shutdownMessage);
}
await WaitForTokenShutdownAsync(token);
}
private static void AttachCtrlcSigtermShutdown(CancellationTokenSource cts, ManualResetEventSlim resetEvent, string shutdownMessage) {
Action ShutDown = () => {
if (!cts.IsCancellationRequested) {
if (!string.IsNullOrWhiteSpace(shutdownMessage)) {
Console.WriteLine(shutdownMessage);
}
try {
cts.Cancel();
} catch (ObjectDisposedException) { }
}
//Wait on the given reset event
resetEvent.Wait();
};
AppDomain.CurrentDomain.ProcessExit += delegate { ShutDown(); };
Console.CancelKeyPress += (sender, eventArgs) => {
ShutDown();
//Don't terminate the process immediately, wait for the Main thread to exit gracefully.
eventArgs.Cancel = true;
};
}
private static async Task WaitForTokenShutdownAsync(CancellationToken token) {
var waitForStop = new TaskCompletionSource<object>();
token.Register(obj => {
var tcs = (TaskCompletionSource<object>)obj;
tcs.TrySetResult(null);
}, waitForStop);
await waitForStop.Task;
}
}
我尝试过改编IConsoleHost
这样的东西,但很快意识到我过度设计了它。将主要部分提取为类似await ConsoleUtil.WaitForShutdownAsync();
Console.ReadLine
然后允许该实用程序像这样使用
public class Program {
public static async Task Main(string[] args) {
//relevant code goes here
//...
//wait for application shutdown
await ConsoleUtil.WaitForShutdownAsync();
}
}
从那里创建一个 systemd ,如下面的链接应该可以让你完成其余的工作
答案 1 :(得分:3)
我能想出的最好结果是基于对其他两个问题的回答:Killing gracefully a .NET Core daemon running on Linux和Is it possible to await an event instead of another async method?
using System;
using System.Runtime.Loader;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public class Program
{
private static TaskCompletionSource<object> taskToWait;
public static void Main(string[] args)
{
taskToWait = new TaskCompletionSource<object>();
AssemblyLoadContext.Default.Unloading += SigTermEventHandler;
Console.CancelKeyPress += new ConsoleCancelEventHandler(CancelHandler);
//eventSource.Subscribe(eventSink) or something...
taskToWait.Task.Wait();
AssemblyLoadContext.Default.Unloading -= SigTermEventHandler;
Console.CancelKeyPress -= new ConsoleCancelEventHandler(CancelHandler);
}
private static void SigTermEventHandler(AssemblyLoadContext obj)
{
System.Console.WriteLine("Unloading...");
taskToWait.TrySetResult(null);
}
private static void CancelHandler(object sender, ConsoleCancelEventArgs e)
{
System.Console.WriteLine("Exiting...");
taskToWait.TrySetResult(null);
}
}
}
答案 2 :(得分:2)
如果你想找到更强大的东西,我发现Github上的一个实现看起来很有希望:.NET Core Application blocks for message-based communication。它使用Host
,HostBuilder
,ApplicationServices
,ApplicationEnvironment
等类来实现消息传递服务。
它似乎还没有为黑盒重用做好准备,但似乎它可能是一个很好的起点。
var host = new HostBuilder()
.ConfigureServices(services =>
{
var settings = new RabbitMQSettings { ServerName = "192.168.80.129", UserName = "admin", Password = "Pass@word1" };
})
.Build();
Console.WriteLine("Starting...");
await host.StartAsync();
var messenger = host.Services.GetRequiredService<IRabbitMQMessenger>();
Console.WriteLine("Running. Type text and press ENTER to send a message.");
Console.CancelKeyPress += async (sender, e) =>
{
Console.WriteLine("Shutting down...");
await host.StopAsync(new CancellationTokenSource(3000).Token);
Environment.Exit(0);
};
...
答案 3 :(得分:2)
使用Visual Studio 2019通过单个代码库为windows
实现Linux守护进程或服务非常容易。只需使用WorkerService
模板创建项目。就我而言
Coraval
库来安排任务。
Program.cs
类
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.File(@"C:\temp\Workerservice\logfile.txt").CreateLogger();
IHost host = CreateHostBuilder(args).Build();
host.Services.UseScheduler(scheduler =>
{
scheduler
.Schedule<ReprocessInvocable>()
.EveryThirtySeconds();
});
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args).UseSystemd() //.UseWindowsService()
.ConfigureServices(services =>
{
services.AddScheduler();
services.AddTransient<ReprocessInvocable>();
});
}
ReprocessInvocable.cs
类
public class ReprocessInvocable : IInvocable
{
private readonly ILogger<ReprocessInvocable> _logger;
public ReprocessInvocable(ILogger<ReprocessInvocable> logger)
{
_logger = logger;
}
public async Task Invoke()
{
//your code goes here
_logger.LogInformation("Information - Worker running at: {time}", DateTimeOffset.Now);
_logger.LogWarning("Warning - Worker running at: {time}", DateTimeOffset.Now);
_logger.LogCritical("Critical - Worker running at: {time}", DateTimeOffset.Now);
Log.Information("Invoke has called at: {time}", DateTimeOffset.Now);
}
}
按照上述代码,对于linux daemon
使用UseSystemd
,对于windows service
使用UseWindowsService
。
答案 4 :(得分:1)
您是否尝试过 Thread.Sleep(Timeout.Infinite)?
using System;
using System.IO;
using System.Threading;
namespace Daemon {
class Program {
static int Main(string[] args) {
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
Log.Critical("Windows is not supported!");
return 1;
}
Agent.Init();
Agent.Start();
if (Agent.Settings.DaemonMode || args.FirstOrDefault() == "daemon") {
Log.Info("Daemon started.");
Thread.Sleep(Timeout.Infinite);
}
Agent.Stop();
}
}
}