Skip to content

Add support to mapping between internal and external IP's and Validate fileds from 'SENTINEL sentinels' command #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,6 @@ $RECYCLE.BIN/
.DS_Store
*.nupkg
/ctstone.Redis/nuget-pack-push.cmd

# Visual studio stuff
.vs/
62 changes: 61 additions & 1 deletion CSRedis/RedisSentinelManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

// http://redis.io/topics/sentinel-clients
Expand All @@ -13,10 +14,13 @@ namespace CSRedis
public class RedisSentinelManager : IDisposable
{
const int DefaultPort = 26379;
private const int NUMBER_OF_CONNECTIONS = 10;
readonly LinkedList<Tuple<string, int>> _sentinels;
string _masterName;
int _connectTimeout;
LinkedList<Tuple<string, RedisConnectionPool>> _pools;
RedisClient _redisClient;
Dictionary<string, string> _hostMapping;

/// <summary>
/// Occurs when the master connection has sucessfully connected
Expand All @@ -30,13 +34,45 @@ public class RedisSentinelManager : IDisposable
public RedisSentinelManager(params string[] sentinels)
{
_sentinels = new LinkedList<Tuple<string, int>>();
_pools = new LinkedList<Tuple<string, RedisConnectionPool>>();
foreach (var host in sentinels)
{
string[] parts = host.Split(':');
string hostname = parts[0].Trim();
int port = Int32.Parse(parts[1]);
Add(hostname, port);
}

}

/// <summary>
/// Return a client from the connection pool
/// </summary>
/// <returns>a master redis client</returns>
public RedisClient GetClient()
{
RedisClient client;
client = GetClientFromPool();
if (!IsMaster(client))
{
Connect(_masterName, _connectTimeout);
client = GetClientFromPool();
}

return client;
}

// return true if client is master
private bool IsMaster(RedisClient client) => client != null && client.Info("role") == "master";

private RedisClient GetClientFromPool()
{
var first = _pools.FirstOrDefault();
if (first != null)
{
return first.Item2.GetClient();
}
return null;
}

/// <summary>
Expand All @@ -48,6 +84,22 @@ public void Add(string host)
Add(host, DefaultPort);
}

/// <summary>
/// Add host mapping for the internal IP returned by Sentinel to the external IP
/// </summary>
/// <param name="hostMapping">Dictionary of sentinel host mapping between internal and external IPs</param>
public void AddHostMapping(Dictionary<string, string> hostMapping)
{
_hostMapping = hostMapping;
}

private string MapHost(string host)
{
if (_hostMapping != null && _hostMapping.ContainsKey(host))
return _hostMapping[host];
return host;
}

/// <summary>
/// Add a new sentinel host
/// </summary>
Expand Down Expand Up @@ -79,6 +131,14 @@ public string Connect(string masterName, int timeout = 200)
throw new IOException("Could not connect to sentinel or master");

_redisClient.ReconnectAttempts = 0;
if(!_pools.Any(c=>c.Item1 == sentinel))
_pools.AddFirst(Tuple.Create(sentinel, new RedisConnectionPool(_redisClient.Host, _redisClient.Port, NUMBER_OF_CONNECTIONS)));
else
{
var pool = _pools.Where(c => c.Item1 == sentinel).First();
_pools.Remove(pool);
_pools.AddFirst(pool);
}
return sentinel;
}

Expand Down Expand Up @@ -137,7 +197,7 @@ string SetMaster(string name, int timeout)
if (master == null)
continue;

_redisClient = new RedisClient(master.Item1, master.Item2);
_redisClient = new RedisClient(MapHost(master.Item1), master.Item2);
_redisClient.Connected += OnConnectionConnected;
if (!_redisClient.Connect(timeout))
continue;
Expand Down
19 changes: 17 additions & 2 deletions CSRedis/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -403,12 +403,26 @@ public RedisServerInfo(SerializationInfo info, StreamingContext context)
Port = info.GetInt32("port");
RunId = info.GetString("runid");
Flags = info.GetString("flags").Split(',');
PendingCommands = info.GetInt64("pending-commands");
PendingCommands = Exists(info, "pending-commands")? info.GetInt64("pending-commands"): info.GetInt64("link-pending-commands");
LastOkPingReply = info.GetInt64("last-ok-ping-reply");
LastPingReply = info.GetInt64("last-ping-reply");
DownAfterMilliseconds = info.GetInt64("down-after-milliseconds");
}

/// <summary>
/// Check if key exists in SerializationInfo
/// </summary>
/// <param name="info">SerializationInfo object</param>
/// <param name="key">The key to check</param>
/// <returns>Return true if key exists in SerializationInfo object</returns>
protected static bool Exists(SerializationInfo info, string key)
{
foreach(var entry in info)
if (entry.Name == key)
return true;
return false;
}

/// <summary>
/// Get or set Redis server name
/// </summary>
Expand Down Expand Up @@ -618,7 +632,8 @@ public class RedisSentinelInfo : RedisServerInfo
public RedisSentinelInfo(SerializationInfo info, StreamingContext context)
: base(info, context)
{
SDownTime = info.GetInt64("s-down-time");
if(Exists(info, "s-down-time"))
SDownTime = info.GetInt64("s-down-time");
LastHelloMessage = info.GetInt64("last-hello-message");
VotedLeader = info.GetString("voted-leader");
VotedLeaderEpoch = info.GetInt64("voted-leader-epoch");
Expand Down