2017-01-04 22 views
11

Sadece uzun süredir çalışan bir CLI uygulaması yazabilirim ve çalıştırabilirim, ancak standart bir uyumlu linux'ların sahip olabileceği tüm beklentilere uymayacağını farz ediyorum. artalan.Net Core ile bir linux daemon nasıl yazılır

en ekosistemlerin örneğin python, kullanabilirsiniz, bunu yapmanın bir en iyi uygulama yolu vardır (etc. terminali I/O sinyalleri göz ardı, Sistem V init süreci ile başlatılan, SIGTERM yanıt) https://pypi.python.org/pypi/python-daemon/

Bunu nasıl yapılacağı ile ilgili bazı belgeler var mı? Net Core?

+0

Sorular SO burada konu kapalıdır. Kapatmak için oy kullanırdım, ancak ödül bunu engelliyor. – JNevill

+3

Listelediğiniz varsayımların çoğu, modern sistemler konusunda gerçekten bir endişe kaynağı değildir. Sistem yöneticileri (fedora/ubuntu/redhat/centos/arch/diğerleri üzerinde kullanılan) gibi süreç yöneticileri arka planda koşuşturmaya özen gösterir ve aslında ön planda duran ve hiçbir şey yapmaya çalışmayan programlarla en iyi şekilde çalışırlar. çatal() veya sinyallerle süslü. – larsks

+1

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

cevap

10

Ben kapatma için nasıl .net çekirdek web barındırma bekler benzer bir fikri çeviriyorlardı konsol uygulamalarında. Ben IConsoleHost gibi bir şey adapte çalıştı ama çabuk mühendislik aşırı Ben fark GitHub'dan üzerine inceleyecek ve

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

nasıl performans özünü çıkarmak mümkün oldu. Console.ReadLine

gibi ameliyat await ConsoleUtil.WaitForShutdownAsync(); gibi bir şey ana bölümden Çıkarılan Bu da yarar var bir oluşturmasını bu

public class Program { 

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

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

gibi kullanılmasına izin gerisini almalısınız aşağıdaki bağlantıyı olduğu gibi systemd yol

Writing a Linux daemon in C#

+1

'async Main' gibi çalışan bir C# 7.1 dil özelliği. Önceki sürümleri kullanıyorsanız, 'ConsoleUtil.Wait()' veya 'ConsoleUtil.WaitForShutdown()' ile 'statik void Main' kullanabilirsiniz. – rianjs

+0

@rianjs bu doğrudur. Bu nedenle, – Nkosi

+0

numaralı kullanımlara dahil ettiğimden, ikinci kod snippet'iniz ('public static async Task Main()') biraz sıra dışıdır, bu yüzden özellikle daha yaygın bir alternatif olarak çağırıyordum. Cevabınız aslında C# 7.1 (!) Olduğunu keşfettim. – rianjs

1

Thread.Sleep (Timeout.Infinite) denediniz mi?

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

Ben diğer iki sorulara cevap dayanmaktadır ile gelen en iyi: Killing gracefully a .NET Core daemon running on Linux ve 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); 
     } 

    } 
} 
1

daha sağlam bir şey bulmaya çalışıyorsanız, buldum bir Github üzerinde umut verici görünen uygulama: .NET Core Application blocks for message-based communication. Bir mesajlaşma servisi uygulamak için Host, HostBuilder, ApplicationServices, ApplicationEnvironment, vb sınıflarını kullanır.

Kara kutu yeniden kullanıma hazır değil, iyi bir başlangıç ​​noktası gibi görünüyor. öğreticiler, yazılım ve dokümantasyon gibi site dışı kaynaklar arayan

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); 
}; 
... 
İlgili konular