Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 80bf9d0

Browse files
committed
Refactor RedisSentinel make more options configurable
1 parent c7d14ac commit 80bf9d0

File tree

1 file changed

+85
-48
lines changed

1 file changed

+85
-48
lines changed

src/ServiceStack.Redis/RedisSentinel.cs

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System;
99
using System.Collections.Generic;
1010
using System.Linq;
11+
using System.Threading;
1112
using ServiceStack;
1213
using ServiceStack.Logging;
1314

@@ -22,15 +23,18 @@ public class RedisSentinel : IRedisSentinel
2223
public static string DefaultMasterName = "mymaster";
2324
public static string DefaultAddress = "127.0.0.1:26379";
2425

26+
private object oLock = new object();
27+
2528
private readonly string masterName;
2629
public string MasterName
2730
{
2831
get { return masterName; }
2932
}
3033

3134
private int failures = 0;
35+
private int totalFailures = 0;
3236
private int sentinelIndex = -1;
33-
private List<string> sentinels;
37+
internal RedisEndpoint[] SentinelHosts { get; private set; }
3438
private RedisSentinelWorker worker;
3539
private static int MaxFailures = 5;
3640

@@ -39,33 +43,51 @@ public string MasterName
3943
public Action<Exception> OnWorkerError { get; set; }
4044
public Action<string, string> OnSentinelMessageReceived { get; set; }
4145

42-
public Dictionary<string, string> IpAddressMap { get; set; }
46+
public Dictionary<string, string> IpAddressMap { get; set; }
47+
48+
public TimeSpan WaitBetweenSentinelLookups { get; set; }
49+
public TimeSpan MaxWaitBetweenSentinelLookups { get; set; }
50+
public TimeSpan SentinelWorkerTimeout { get; set; }
51+
52+
public bool ResetWhenSubjectivelyDown { get; set; }
53+
public bool ResetWhenObjectivelyDown { get; set; }
4354

4455
public RedisSentinel(string sentinelHost = null, string masterName = null)
4556
: this(new[] { sentinelHost ?? DefaultAddress }, masterName ?? DefaultMasterName) { }
4657

4758
public RedisSentinel(IEnumerable<string> sentinelHosts, string masterName = null)
4859
{
49-
this.sentinels = sentinelHosts != null ? sentinelHosts.ToList() : null;
50-
if (sentinelHosts == null || sentinels.Count == 0)
60+
this.SentinelHosts = sentinelHosts != null
61+
? sentinelHosts.Map(x => x.ToRedisEndpoint(defaultPort:RedisNativeClient.DefaultPortSentinel)).ToArray()
62+
: null;
63+
64+
if (SentinelHosts == null || SentinelHosts.Length == 0)
5165
throw new ArgumentException("sentinels must have at least one entry");
5266

5367
this.masterName = masterName ?? DefaultMasterName;
5468
IpAddressMap = new Dictionary<string, string>();
5569
RedisManagerFactory = (masters,slaves) => new PooledRedisClientManager(masters, slaves);
70+
ResetWhenObjectivelyDown = true;
71+
ResetWhenSubjectivelyDown = true;
72+
SentinelWorkerTimeout = TimeSpan.FromMilliseconds(100);
73+
WaitBetweenSentinelLookups = TimeSpan.FromMilliseconds(250);
74+
MaxWaitBetweenSentinelLookups = TimeSpan.FromSeconds(60);
5675
}
5776

5877
/// <summary>
5978
/// Initialize Sentinel Subscription and Configure Redis ClientsManager
6079
/// </summary>
6180
public IRedisClientsManager Start()
6281
{
63-
GetValidSentinel();
82+
lock (oLock)
83+
{
84+
GetValidSentinel();
6485

65-
if (this.RedisManager == null)
66-
throw new ApplicationException("Unable to resolve sentinels!");
86+
if (this.RedisManager == null)
87+
throw new ApplicationException("Unable to resolve sentinels!");
6788

68-
return this.RedisManager;
89+
return this.RedisManager;
90+
}
6991
}
7092

7193
public Func<string, string> HostFilter { get; set; }
@@ -102,12 +124,12 @@ public SentinelInfo ResetClients()
102124

103125
private IRedisClientsManager CreateRedisManager(SentinelInfo sentinelInfo)
104126
{
105-
var redisManager = RedisManagerFactory(
106-
ConfigureHosts(sentinelInfo.RedisMasters),
107-
ConfigureHosts(sentinelInfo.RedisSlaves));
127+
var masters = ConfigureHosts(sentinelInfo.RedisMasters);
128+
var slaves = ConfigureHosts(sentinelInfo.RedisSlaves);
129+
var redisManager = RedisManagerFactory(masters, slaves);
108130

109-
//var hasRedisResolver = (IHasRedisResolver)redisManager;
110-
//hasRedisResolver.RedisResolver = new RedisSentinelResolver(redisSentinel);
131+
var hasRedisResolver = (IHasRedisResolver)redisManager;
132+
hasRedisResolver.RedisResolver = new RedisSentinelResolver(this, masters, slaves);
111133

112134
var canFailover = redisManager as IRedisFailover;
113135
if (canFailover != null && this.OnFailover != null)
@@ -129,12 +151,12 @@ private RedisSentinelWorker GetValidSentinel()
129151

130152
RedisException lastEx = null;
131153

132-
while (this.RedisManager == null && ShouldRetry())
154+
while (this.worker == null && ShouldRetry())
133155
{
134156
try
135157
{
136158
this.worker = GetNextSentinel();
137-
this.RedisManager = GetRedisManager();
159+
GetRedisManager();
138160
this.worker.BeginListeningForConfigurationChanges();
139161
return this.worker;
140162
}
@@ -144,25 +166,36 @@ private RedisSentinelWorker GetValidSentinel()
144166
OnWorkerError(ex);
145167

146168
lastEx = ex;
147-
if (this.worker != null)
148-
this.worker.Dispose();
149-
150169
this.failures++;
170+
this.totalFailures++;
151171
}
152172
}
153173

154-
throw new RedisException("RedisSentinel is not accessible", lastEx);
174+
this.failures = 0; //reset
175+
Thread.Sleep(WaitBetweenSentinelLookups);
176+
177+
throw new RedisException("No Redis Sentinels were available", lastEx);
155178
}
156179

157-
public string GetMasterHost()
180+
public RedisEndpoint GetMaster()
158181
{
159182
var sentinelWorker = GetValidSentinel();
183+
lock (sentinelWorker)
184+
{
185+
var host = sentinelWorker.GetMasterHost(masterName);
186+
return host != null
187+
? (HostFilter != null ? HostFilter(host) : host).ToRedisEndpoint()
188+
: null;
189+
}
190+
}
160191

192+
public List<RedisEndpoint> GetSlaves()
193+
{
194+
var sentinelWorker = GetValidSentinel();
161195
lock (sentinelWorker)
162196
{
163-
var parts = sentinelWorker.GetMasterHost(masterName);
164-
var host = "{0}:{1}".Fmt(parts[0], parts[1]);
165-
return host;
197+
var hosts = sentinelWorker.GetSlaveHosts(masterName);
198+
return ConfigureHosts(hosts).Map(x => x.ToRedisEndpoint());
166199
}
167200
}
168201

@@ -173,29 +206,33 @@ public string GetMasterHost()
173206
/// <remarks>This will be true if the failures is less than either RedisSentinel.MaxFailures or the # of sentinels, whatever is greater</remarks>
174207
private bool ShouldRetry()
175208
{
176-
return this.failures < Math.Max(MaxFailures, this.sentinels.Count);
209+
return this.failures < Math.Max(MaxFailures, this.SentinelHosts.Length);
177210
}
178211

179212
private RedisSentinelWorker GetNextSentinel()
180213
{
181-
sentinelIndex++;
214+
lock (oLock)
215+
{
216+
sentinelIndex++;
182217

183-
if (sentinelIndex >= sentinels.Count)
184-
sentinelIndex = 0;
218+
if (this.worker != null)
219+
{
220+
this.worker.Dispose();
221+
this.worker = null;
222+
}
185223

186-
var sentinelWorker = new RedisSentinelWorker(this, sentinels[sentinelIndex])
187-
{
188-
OnSentinelError = OnSentinelError
189-
};
224+
if (sentinelIndex >= SentinelHosts.Length)
225+
sentinelIndex = 0;
226+
227+
var sentinelWorker = new RedisSentinelWorker(this, SentinelHosts[sentinelIndex])
228+
{
229+
OnSentinelError = OnSentinelError
230+
};
190231

191-
return sentinelWorker;
232+
return sentinelWorker;
233+
}
192234
}
193235

194-
/// <summary>
195-
/// Raised if there is an error from a sentinel worker
196-
/// </summary>
197-
/// <param name="sender"></param>
198-
/// <param name="e"></param>
199236
private void OnSentinelError(Exception ex)
200237
{
201238
if (this.worker != null)
@@ -205,27 +242,26 @@ private void OnSentinelError(Exception ex)
205242
if (OnWorkerError != null)
206243
OnWorkerError(ex);
207244

208-
// dispose the worker
209-
this.worker.Dispose();
210-
211-
// get a new worker and start looking for more changes
212245
this.worker = GetNextSentinel();
213246
this.worker.BeginListeningForConfigurationChanges();
214247
}
215248
}
216249

217250
public SentinelInfo GetSentinelInfo()
218251
{
219-
return GetValidSentinel().GetSentinelInfo();
252+
var sentinelWorker = GetValidSentinel();
253+
lock (sentinelWorker)
254+
{
255+
return sentinelWorker.GetSentinelInfo();
256+
}
220257
}
221258

222259
public void Dispose()
223260
{
224-
if (worker != null)
225-
{
226-
worker.Dispose();
227-
worker = null;
228-
}
261+
if (worker == null) return;
262+
263+
worker.Dispose();
264+
worker = null;
229265
}
230266
}
231267
}
@@ -236,8 +272,9 @@ public class SentinelInfo
236272
public string[] RedisMasters { get; set; }
237273
public string[] RedisSlaves { get; set; }
238274

239-
public SentinelInfo(string masterName, List<string> redisMasters, List<string> redisSlaves)
275+
public SentinelInfo(string masterName, IEnumerable<string> redisMasters, IEnumerable<string> redisSlaves)
240276
{
277+
MasterName = masterName;
241278
RedisMasters = redisMasters != null ? redisMasters.ToArray() : new string[0];
242279
RedisSlaves = redisSlaves != null ? redisSlaves.ToArray() : new string[0];
243280
}

0 commit comments

Comments
 (0)