|
1 | | -# Topshelf.Leader |
| 1 | +# Topshelf.Leader |
| 2 | + |
| 3 | +An extension method to the Topshelf `ServiceConfigurator<T>` class that adds Leader checking to the service startup. |
| 4 | + |
| 5 | +## Getting started |
| 6 | +``` |
| 7 | +Install-Package Topshelf.Leader |
| 8 | +``` |
| 9 | + |
| 10 | +Once the package is installed, create a Console application and wireup the Topshelf service as you normally would except for the `WhenStarted()` method - this should no longer be used. |
| 11 | + |
| 12 | +You should use the `WhenStartedAsLeader()` method that Topshelf.Leader provides instead which constains its own version of the `WhenStarted()` method, one with cancellation token support. |
| 13 | + |
| 14 | +### Example |
| 15 | +```c# |
| 16 | +using Topshelf.Leader; |
| 17 | + |
| 18 | +public class Program |
| 19 | +{ |
| 20 | + static void Main(string[] args) |
| 21 | + { |
| 22 | + var rc = HostFactory.Run(x => |
| 23 | + { |
| 24 | + x.Service<TheService>(s => |
| 25 | + { |
| 26 | + s.WhenStartedAsLeader(builder => |
| 27 | + { |
| 28 | + builder.WhenStarted(async (service, token) => |
| 29 | + { |
| 30 | + await service.Start(token); |
| 31 | + }); |
| 32 | + }); |
| 33 | + s.ConstructUsing(name => new TheService()); |
| 34 | + s.WhenStopped(service => service.Stop()); |
| 35 | + }); |
| 36 | + } |
| 37 | + } |
| 38 | +} |
| 39 | +``` |
| 40 | + |
| 41 | +## How does it work? |
| 42 | + |
| 43 | +The `WhenStarted()` method will be executed when the service discovers that it is the current leader. If that situation changes the cancellation token will be set to cancelled. You decide how to handle this situation, throw an exception, exit gracefully or even carry on whatever you were doing - that's entirely up to you. |
| 44 | + |
| 45 | +### Example of a service which supports leadership change |
| 46 | +```c# |
| 47 | + public class TestService |
| 48 | + { |
| 49 | + public async Task Start(CancellationToken stopToken) |
| 50 | + { |
| 51 | + while (!stopToken.IsCancellationRequested) |
| 52 | + { |
| 53 | + // do your work here, if it's async pass the stopToken to it |
| 54 | + } |
| 55 | + } |
| 56 | + } |
| 57 | +``` |
| 58 | + |
| 59 | +## Leadership Manager |
| 60 | + |
| 61 | +The responsibility for deciding who is the leader, and maintaining that status is delegated to any class which implements the `ILeaderManager` interface. You configure which manager to use during the configuration stage. If one isn't supplied then an in memory manager is used - this is not muti-process aware so is **not suitable for production use - only for testing**. |
| 62 | + |
| 63 | +### Configuring the leadership manager |
| 64 | +```c# |
| 65 | +var rc = HostFactory.Run(x => |
| 66 | +{ |
| 67 | + x.Service<TheService>(s => |
| 68 | + { |
| 69 | + s.WhenStartedAsLeader(builder => |
| 70 | + { |
| 71 | + builder.WhenStarted(async (service, token) => |
| 72 | + { |
| 73 | + await service.Start(token); |
| 74 | + }); |
| 75 | + builder.WithLeadershipManager(new YourManagerHere()); |
| 76 | + }); |
| 77 | + s.ConstructUsing(name => new TheService()); |
| 78 | + s.WhenStopped(service => service.Stop()); |
| 79 | + }); |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +### Example of a simple leadership manager |
| 84 | +```c# |
| 85 | +public class InMemoryLeadershipManager : ILeadershipManager |
| 86 | +{ |
| 87 | + private string owningNodeId; |
| 88 | + |
| 89 | + public InMemoryLeadershipManager(string owningNodeId) |
| 90 | + { |
| 91 | + this.owningNodeId = owningNodeId; |
| 92 | + } |
| 93 | + |
| 94 | + public void AssignLeader(string newLeaderId) |
| 95 | + { |
| 96 | + this.owningNodeId = newLeaderId; |
| 97 | + } |
| 98 | + |
| 99 | + public Task<bool> AcquireLock(string nodeId, CancellationToken token) |
| 100 | + { |
| 101 | + return Task.FromResult(nodeId == owningNodeId); |
| 102 | + } |
| 103 | + |
| 104 | + public Task<bool> RenewLock(string nodeId, CancellationToken token) |
| 105 | + { |
| 106 | + return Task.FromResult(nodeId == owningNodeId); |
| 107 | + } |
| 108 | +} |
| 109 | +``` |
0 commit comments