Last active
July 4, 2017 21:29
-
-
Save trbngr/5083266 to your computer and use it in GitHub Desktop.
EventStore as a windows service w/ TopShelf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<configuration> | |
<configSections> | |
<section name="eventStore" type="EventStoreService.EventStoreServiceConfiguration, EventStoreService, Version=1.0.0.0, Culture=neutral" /> | |
</configSections> | |
<eventStore> | |
<instance name="Production" tcpPort="4532" httpPort="5500" dbPath="F:\production" /> | |
<instance name="Staging" tcpPort="5532" httpPort="6500" dbPath="F:\staging" /> | |
</eventStore> | |
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Net; | |
namespace EventStoreService | |
{ | |
public class EventStoreService | |
{ | |
private readonly List<Process> _processes; | |
private readonly IPAddress _address; | |
private readonly ServiceInstanceCollection _instances; | |
public EventStoreService(IPAddress address, ServiceInstanceCollection instances) | |
{ | |
_address = address; | |
_instances = instances; | |
_processes = new List<Process>(); | |
} | |
public void Start() | |
{ | |
string file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "eventstore/EventStore.SingleNode.exe"); | |
foreach (ServiceInstance instance in _instances) | |
{ | |
var info = instance.GetProcessStartInfo(file, _address); | |
var process = Process.Start(info); | |
process.Exited += (sender, args) => Stop(); | |
_processes.Add(process); | |
} | |
} | |
public void Stop() | |
{ | |
_processes.ForEach(p => | |
{ | |
p.Refresh(); | |
if (p.HasExited) return; | |
p.Kill(); | |
p.WaitForExit(); | |
p.Dispose(); | |
}); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Configuration; | |
using System.Diagnostics; | |
using System.Net; | |
using System.Text; | |
namespace EventStoreService | |
{ | |
public class EventStoreServiceConfiguration : ConfigurationSection | |
{ | |
[ConfigurationProperty("", IsDefaultCollection = true, IsKey = false, IsRequired = true)] | |
public ServiceInstanceCollection Instances | |
{ | |
get { return (ServiceInstanceCollection) this[""]; } | |
set { this[""] = value; } | |
} | |
} | |
public class ServiceInstanceCollection : ConfigurationElementCollection | |
{ | |
protected override string ElementName | |
{ | |
get { return "instance"; } | |
} | |
public override ConfigurationElementCollectionType CollectionType | |
{ | |
get { return ConfigurationElementCollectionType.BasicMap; } | |
} | |
public ServiceInstance this[int index] | |
{ | |
get { return BaseGet(index) as ServiceInstance; } | |
} | |
protected override ConfigurationElement CreateNewElement() | |
{ | |
return new ServiceInstance(); | |
} | |
protected override object GetElementKey(ConfigurationElement element) | |
{ | |
return ((ServiceInstance) element).Name; | |
} | |
protected override bool IsElementName(string elementName) | |
{ | |
return !String.IsNullOrEmpty(elementName) && elementName == ElementName; | |
} | |
} | |
public class ServiceInstance : ConfigurationElement | |
{ | |
[ConfigurationProperty("name", IsRequired = true)] | |
public string Name | |
{ | |
get { return (string) this["name"]; } | |
set { this["name"] = value; } | |
} | |
[ConfigurationProperty("tcpPort", IsRequired = true)] | |
public int TcpPort | |
{ | |
get { return (int) this["tcpPort"]; } | |
set { this["tcpPort"] = value; } | |
} | |
[ConfigurationProperty("httpPort", IsRequired = true)] | |
public int HttpPort | |
{ | |
get { return (int) this["httpPort"]; } | |
set { this["httpPort"] = value; } | |
} | |
[ConfigurationProperty("dbPath", IsRequired = true)] | |
public string DbPath | |
{ | |
get { return (string) this["dbPath"]; } | |
set { this["dbPath"] = value; } | |
} | |
public ProcessStartInfo GetProcessStartInfo(string file, IPAddress address) | |
{ | |
var arguments = GetProcessArguments(address); | |
return new ProcessStartInfo(file, arguments) | |
{ | |
UseShellExecute = false | |
}; | |
} | |
private string GetProcessArguments(IPAddress address) | |
{ | |
if (address == null) throw new ArgumentNullException("address"); | |
var sb = new StringBuilder(); | |
sb.AppendFormat("--ip {0} ", address); | |
sb.AppendFormat("--tcp-port {0} ", TcpPort); | |
sb.AppendFormat("--http-port {0} ", HttpPort); | |
sb.AppendFormat("--db {0}", DbPath); | |
return sb.ToString(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@ECHO off | |
set exe="%~dp0EventStoreService.exe" | |
%exe% install | |
pause |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Configuration; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Sockets; | |
using Topshelf; | |
namespace EventStoreService | |
{ | |
public class Program | |
{ | |
public static void Main() | |
{ | |
var configuration = (EventStoreServiceConfiguration)ConfigurationManager.GetSection("eventStore"); | |
var address = GetIPAddress(); | |
HostFactory.Run(x => | |
{ | |
x.RunAsLocalSystem(); | |
x.StartAutomatically(); | |
x.EnableShutdown(); | |
x.EnableServiceRecovery(c => c.RestartService(1)); | |
x.Service<EventStoreService>(s => | |
{ | |
s.ConstructUsing(name => new EventStoreService(address, configuration.Instances)); | |
s.WhenStarted(tc => tc.Start()); | |
s.WhenStopped(tc => tc.Stop()); | |
}); | |
x.SetDescription("EventDay EventStore Service"); | |
x.SetDisplayName("EventDay EventStore"); | |
x.SetServiceName("EventDayEventStore"); | |
}); | |
Console.ReadLine(); | |
} | |
private static IPAddress GetIPAddress() | |
{ | |
string hostName = Dns.GetHostName(); | |
return Dns.GetHostAddresses(hostName).First(address => | |
{ | |
if (address.AddressFamily != AddressFamily.InterNetwork) | |
return false; | |
return !Equals(address, IPAddress.Loopback); | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice and clean solution. I hope you didn't mind I copied basically everything and put it here: https://github.com/mastoj/EventStoreWinServiceWrapper