2017-01-04 2 views
11

장기 실행 CLI 응용 프로그램을 작성하고 실행할 수는 있지만 표준 호환 Linux에 대한 모든 기대치를 충족시키지 못한다고 가정하고 있습니다. 데몬은.Net Core와 함께 리눅스 데몬을 작성하는 방법

대부분의 생태계 예를 들어, 파이썬에서, 당신이 사용할 수있는,이 일을위한 최선의 방법이 (etc.를 터미널 I/O 신호를 무시, 시스템 V의 init 프로세스에 의해 시작, SIGTERM에 응답) https://pypi.python.org/pypi/python-daemon/

.Net Core에서이 작업을 수행하는 방법에 대한 문서가 있습니까?

+0

자습서, 소프트웨어 및 설명서와 같은 오프 사이트 리소스를 찾는 질문은 여기에서 나와 있습니다. 나는 투표에 투표 할 것이지만 현상금으로 막을 수 있습니다. – JNevill

+3

당신이 열거 한 가정의 대부분은 현대 시스템에서 실제로 관심사가 아닙니다. fedd/우분투/redhat/centos/arch/others에서 사용되는 systemd와 같은 프로세스 관리자는 백그라운드에서 실행중인 작업을 처리하며 실제로 포 그라운드에 머물면서 아무 것도 시도하지 않는 프로그램에서 가장 잘 작동합니다 포크() 또는 신호로 공상. – larsks

+1

https://developers.redhat.com/blog/2017/06/07/writing-a-linux-daemon-in-c/ – Nkosi

답변

10

내가 종료에 대한 방법 .NET의 핵심 웹 호스트 대기 유사한 아이디어 장난 삼아 생각해 콘솔 응용 프로그램에서. 나는 내가 IConsoleHost 같은 적응 시도했지만 빨리 엔지니어링을 통해-I는 것을 깨달았다 GitHub의에 그것을 검토하고 그들이

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

https://github.com/aspnet/Hosting/blob/15008b0b7fcb54235a9de3ab844c066aaf42ea44/src/Microsoft.AspNetCore.Hosting/WebHostExtensions.cs#L86

Run

을 수행하는 방법의 요점을 추출 할 수이었다. Console.ReadLine

처럼 운영 await ConsoleUtil.WaitForShutdownAsync(); 같은 무언가로 주요 부분을 추출이 다음 유틸리티가 거기 를 만드는에서이

public class Program { 

    public static async Task Main(string[] args) { 
     //relevant code goes here 
     //... 

     //wait for application shutdown 
     await ConsoleUtil.WaitForShutdownAsync(); 
    } 
} 

처럼 사용할 수 당신에게 나머지를 받아야 다음 링크로을 systemd 방식

Writing a Linux daemon in C#

+1

'async Main'은 C# 7.1 언어 기능입니다. 이전 버전을 사용하고 있다면'ConsoleUtil.Wait()'또는'ConsoleUtil.WaitForShutdown()'과 함께'static void Main'을 사용할 수 있습니다. – rianjs

+0

@rianjs입니다. 그 이유는 내가 그것을 util에 포함 시켰기 때문이다. – Nkosi

+0

네, 두 번째 코드 스 니펫 ('public static async Task Main()')은 좀 특이한 것이기 때문에 좀 더 일반적인 대안으로 명시했다. 당신의 대답은 실제로 내가 C# 7.1 (!)이 있다는 것을 발견했습니다. – rianjs

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(); 
     } 
    } 
} 
2

내가 다른 두 질문에 대한 답변을 기반으로 가지고 올 수있는 최선 : Killing gracefully a .NET Core daemon running on LinuxIs 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); 
     } 

    } 
} 
1

당신이 더 강력한 무언가를 찾기 위해 노력하는 경우, 내가 발견되는 유망한 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 = "[email protected]" }; 
      }) 
      .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); 
}; 
... 
관련 문제