.NET Core + C#でLinuxのDeamon作れないかなと思ったら、思ったより簡単に作れるようになっていたので覚え書き。Program.csとDaemonService.csの中味はほぼ定型なのでサンプルからそのまま流用します。
以下のサンプルは.NET Core 3.1で記述しています。
// Program.cs
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace DaemonSample
{
class Program
{
public static async Task Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
.ConfigureServices((hostContext, services) =>
{
services.AddOptions();
services.Configure<DaemonConfig>(hostContext.Configuration.GetSection("Daemon"));
services.AddSingleton<IHostedService, DaemonService>();
})
.ConfigureLogging((hostingContext, logging) => {
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
});
await builder.RunConsoleAsync();
}
}
}
// DaemonService.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DaemonSample
{
class DaemonService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private readonly IOptions<DaemonConfig> _config;
public DaemonService(ILogger<DaemonService> logger, IOptions<DaemonConfig> config)
{
_logger = logger;
_config = config;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Starting daemon: " + _config.Value.DaemonName);
_logger.LogInformation("arg1:{0}", _config.Value.arg1);
_logger.LogInformation("arg2:{0}", _config.Value.arg2);
// ここでサーバー本体の処理をTaskとして起動します。
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Stopping daemon.");
return Task.CompletedTask;
}
public void Dispose()
{
_logger.LogInformation("Disposing....");
}
}
}
// DaemonConfig.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace DaemonSample
{
class DaemonConfig
{
public string DaemonName { get; set; }
public string arg1{ get; set; }
public string arg2{ get; set; }
}
}
起動時のコマンドラインは下記の通り。コマンドライン引数の受け渡し方が非常に独特です。
/usr/bin/dotnet run --project deamonsample --configuration Release --Daemon:DaemonName="Sample Daemon" Daemon:arg1="argument 1" Daemon:arg2="argument 2"
Demonとして起動するにはsystemdを使用します。/etc/systemd/systemにdeamonsample.serviceファイルを作成し、下記の様に記述します。
[Unit]
Description=deamon sample
After=network.target
[Service]
ExecStart=/usr/bin/dotnet run --project atozipsrv --configuration Release --Daemon:DaemonName="deamonsample " Daemon:arg1="argument 1" Daemon:arg2="argument 2"
WorkingDirectory=/usr/src/deamonsample
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=atozip
Environment=DOTNET_CLI_HOME=/var/src/deamonsample
[Install]
WantedBy=multi-user.target
後は通常のsystemdのDeamon同様に以下のコマンドで起動します。
$ systemctl start deamonsample
参考:Creating a Daemon with .NET Core (Part 1)