TopShelf - хорошо сделанная библиотека для создания windows сервисов. Чтобы с ней сделать windows service достаточно просто добавить пакет Topshelf из NuGet и написать несколько строчек в Main(). Получается сервис, который можно запускать как консольное приложение, а можно установить в службы, запустив с командой install.
Orleans - интересная реализация системы акторов для .NET. Некоторых вещей в orleans пока сильно не хватает, например возможности управлять запущенной нодой (Silo). То есть, чтобы обновить код работающего под управлением Orleans приложения, мне придётся сначала грохнуть процесс, потом переписать файлы и запустить процесс заново.
Решил совместить оба варианта, чтобы можно было управлять запущенной нодой как windows-сервисом и останавливать-запускать из командной строки. Заодно появляется возможность разворачивать сервис с помощью, например, удобного Octopus Deploy.
Чтобы не дублировать код, берём из sdk orleans готовый OrleansHost.exe и добавляем как reference в наш проект. Для того, чтобы остановить запуск сервиса при ошибках запуска Orleans, используем предоставленный TopShelf экземпляр HostControl, и вызываем .Stop(), после того как отработает OrleansHost.Run().
internal class Program | |
{ | |
private static void Main(string[] args) | |
{ | |
HostFactory.Run(x => | |
{ | |
x.EnableServiceRecovery(a => | |
{ | |
a.OnCrashOnly(); | |
a.RestartService(1); | |
}); | |
x.Service<WindowsServerHost>(s => | |
{ | |
s.ConstructUsing(name => new WindowsServerHost()); | |
s.WhenStarted((tc, hc) => | |
{ | |
if (!tc.ParseArguments(SiloStartupArgs())) | |
{ | |
tc.PrintUsage(); | |
} | |
else | |
{ | |
Task.Factory.StartNew(() => | |
{ | |
tc.Init(); | |
tc.Run(); | |
hc.Stop(); | |
}); | |
} | |
return true; | |
}); | |
s.WhenStopped(tc => { tc?.Dispose(); }); | |
}); | |
x.RunAsLocalSystem(); | |
x.SetDescription("my orleans service"); | |
x.SetDisplayName("testorleans"); | |
x.SetServiceName("testorleans"); | |
}); | |
} | |
private static string[] SiloStartupArgs() | |
{ | |
var siloName = ConfigurationManager.AppSettings.Get("SiloName"); | |
if (!string.IsNullOrEmpty(siloName)) | |
{ | |
return new[] {siloName}; | |
} | |
return new string[] {}; | |
} | |
} |