복붙노트

[REDIS] C에서 # StackExchange / 센티넬와 레디 스 페일 오버

REDIS

C에서 # StackExchange / 센티넬와 레디 스 페일 오버

우리는 현재 레디 스 2.8.4과 StackExchange.Redis를 사용하여 (그리고 사랑)하지만 순간에 하드웨어 오류 등에 대한 보호의 어떤 종류가없는 것입니다. 나는 우리가 마스터 / 슬레이브 및 모니터링 감시를함으로써 작업 솔루션을 얻으려고하지만 확실히 거기에 얻을 수 없다 내가 검색 한 후 실제 포인터를 찾을 수 없습니다입니다.

그래서 현재 우리는 여기까지 가지고있다 :

우리는 각 노드 (리눅스들에 의해 설정)에 3 개 레디 스 서버와 감시가 : devredis01 : 6383 (마스터) devredis02 : 6383 (슬레이브) devredis03 : 6383 (슬레이브) devredis01 : 26,379 (감시) devredis02 : 26,379 (감시) devredis03 : 26,379 (감시)

I /를 레디 스 서버와 쓰기에 StackExchange 클라이언트를 연결 읽고 모두 레디 스 데스크톱 관리자를 사용하여 인스턴스를 레디 스를 통해 데이터가 복제되고 있음을 확인할 수 있어요.

또한 노예 등을 요청, 마스터 노드를 레디 스를 요청, 다른 ConnectionMultiplexer를 사용하여 감시 서비스에 쿼리 설정을 연결할 수 있습니다

우리는 또한 마스터 레디 스 노드를 죽이고 노예의 하나가 다른 슬레이브 마스터 복제로 승진 일까지 계속됩니다 확인할 수 있습니다. 우리는 마스터에 다시 연결하려고 레디 스 연결을 관찰하고, 내가 쓸 수 ConnectionMultiplexer을 다시하는 경우도 / 새로 승진 마스터 다시 읽고 슬레이브에서 읽을 수 있습니다.

여태까지는 그런대로 잘됐다!

내가 부족 비트 프로덕션 시스템에서 모두 함께 그것을 가지고 어떻게 무엇입니까?

나는 감시에서 레디 스 엔드 포인트를 받고, 2 ConnectionMultiplexers을 이용해야 하는가? 무엇 정확히 내가 노드가 추락했습니다 감지하려면 어떻게해야합니까? 캔 StackExchange 자동으로 나를 위해이 작업을 수행하거나 내 레디 스의 ConnectionMultiplexer을 다시 연결할 수 있도록 이벤트를 전달 하는가? 나는 ConnectionFailed 이벤트를 처리하고 ConnectionMuliplexer 새로운 마스터가 무엇인지 찾기위한 다음 순서로 다시 연결해야 하는가? 내가 쓰기에 대한 시도를 다시 연결하고 아마도 동안 손실됩니다?

나는 여기 난 그냥 모두 함께 넣어 사투를 벌인거야 매우 ​​분명 뭔가가 아니에요 바랍니다.

사전에 감사합니다!

해결법

  1. ==============================

    1.난 그냥이 질문을, 나는 대답 우리의 코드 (클라이언트)가 새 마스터 서버가있는 현재 마스터가 다운 될 때 지금 알고 수행하는 방법에 대한 질문을 믿고 당신과 나의 비슷한 질문을 찾았나요?

    난 그냥이 질문을, 나는 대답 우리의 코드 (클라이언트)가 새 마스터 서버가있는 현재 마스터가 다운 될 때 지금 알고 수행하는 방법에 대한 질문을 믿고 당신과 나의 비슷한 질문을 찾았나요?

    새로운 레디 스 마스터가 센티넬를 사용하는 클라이언트를 확인하는 방법

    분명히 당신은 가입과 센티넬에서 이벤트를 수신해야합니다. 차종의 의미는 .. 난 그냥 더 능률적 인 방법이라고 생각 했어요.

    나는이 당신을 위해 수행 아마 프록시 역할 및 Linux 용 Twemproxy 대해 뭔가를 읽어? 하지만 Windows 용 레디 스있었습니다 윈도우 옵션을 찾기 위해 노력했다. 그게 수행해야하는 승인 된 방법 경우 우리는 단지 리눅스로 이동 할 수 있습니다.

  2. ==============================

    2.나는 리눅스 사람이 시나리오를 테스트하고이 구현의 C #을 측면에 작업을 다음과 같은 방법을 사용하고 지난 주 시간을 보낼 수 있었다 :

    나는 리눅스 사람이 시나리오를 테스트하고이 구현의 C #을 측면에 작업을 다음과 같은 방법을 사용하고 지난 주 시간을 보낼 수 있었다 :

    나는 일반적으로 내가 작업과 레디 스 마스터를 잃을 후 약 5 초를 재구성하고 있음을 찾을 수 있습니다. 이 기간 동안 나는 쓸 수 있지만 (당신은 노예를 읽을 수 있기 때문에) 나는 읽을 수 있습니다. 5 초 매우 신속하게 데이터 업데이트 이후 우리에게 확인하고 몇 초 후에 오래된된다 (그리고 이후에 덮어 쓰기됩니다).

    나는 확실하지 않았다 한 가지에 대해 내가 인스턴스가 다운 될 때이 ConnectionMultiplexer을 레디 스에서 레디 스 서버를 제거하거나 연결을 다시 시도를 계속 할 것인지 여부였다. 나는 그것이 다시 오는대로 즉시 다시 노예로 믹스에 온다 재 시도를 떠나기로 결정했다. 나는 함께하고 시도되는 연결하지 않고 테스트 어떤 성능을했고, 약간의 차이를 보였다. 어쩌면 누군가는이 올바른 접근 방식인지 명확히 할 수 있습니다.

    모든 이제 다음 이전에 있던 마스터가 혼란을 일으킬 것으로 보인다 않은 인스턴스를 다시 가져 - 나는 쓰기에서 예외가 나타날 것까지가 돌아왔다 몇 초 후 - "READONLY"나는 노예에 쓸 수 없습니다 제안. 이 희귀했지만, 난 구성을 호출 내 "포괄"접근 방식은 () 연결 상태 변화가 12 초이 문제를 잡은 것으로 나타났다. 구성 ()를 호출하면 매우 저렴하기 때문에 두 번 여부에 관계없이 그것의 필요한 듯 OK의를 호출하는 것 같다.

    지금은 노예를 가지고 나는 나를 행복하게 노예로 키 검사를 수행 내 데이터 정리 코드의 일부를 오프로드있다.

    나는 꽤 만족하고있어 모두 모두, 그것은 완벽 하진하지만 뭔가 아주 드물게 더 좋은 충분의 일이 없어야한다 그.

  3. ==============================

    3.나는 여러 가지 이유로, 원래의 대답은 다소 변경되었습니다, 우리의 레디 스 래퍼를 포함하고있다 :

    나는 여러 가지 이유로, 원래의 대답은 다소 변경되었습니다, 우리의 레디 스 래퍼를 포함하고있다 :

    차라리이 다른 우리의 감시 / 레디 스 구성 이상의 아무것도 다운 생각한다. 어느 쪽이든, 그것은 단지 파괴적인 테스트에도 불구하고 완벽하게 믿을 수 없었다. 우리는 "너무 민감"존재하고 거기있을 때 장애 조치를 호출 감시하기 때문에 시간 제한을 증가했다 이후 마스터 변경 메시지가 오랜 시간이 걸렸습니다, 어떤 추가. 나는 가상 환경에서 실행하는 것도 문제를 더욱 악화 생각합니다.

    대신 구독을 듣고 지금 우리는 단순히 쓰기 테스트를 5 초마다 시도도 술집 / 서브에 대한 검사를 "마지막 메시지가 수신 '이있다. 우리가 어떤 문제가 발생하면 우리는 완전히 연결을 제거하고 다시 작성. 그것은 잔인한 것 같지만 실제로는 꽤 빨리 여전히 빠른 감시에서 마스터 변경 메시지를 기다리는 것보다의 ...

    이것은 다양한 확장 방법 등 다른 클래스하지 않고 컴파일되지 않습니다,하지만 당신은 아이디어를 얻을.

    namespace Smartodds.Framework.Redis
    {
        public class RedisClient : IDisposable
        {
            public RedisClient(RedisEnvironmentElement environment, Int32 databaseId)
            {
                m_ConnectTimeout = environment.ConnectTimeout;
                m_Timeout = environment.Timeout;
                m_DatabaseId = databaseId;
                m_ReconnectTime = environment.ReconnectTime;
                m_CheckSubscriptionsTime = environment.CheckSubscriptions;
                if (environment.TestWrite == true)
                {
                    m_CheckWriteTime = environment.TestWriteTime;
                }
    
                environment.Password.ToCharArray().ForEach((c) => m_Password.AppendChar(c));
    
                foreach (var server in environment.Servers)
                {
                    if (server.Type == ServerType.Redis)
                    {
                        // will be ignored if sentinel servers are used
                        m_RedisServers.Add(new RedisConnection { Address = server.Host, Port = server.Port });
                    }
                    else
                    {
                        m_SentinelServers.Add(new RedisConnection { Address = server.Host, Port = server.Port });
                    }
                }
            }
    
            public bool IsSentinel { get { return m_SentinelServers.Count > 0; } }
    
            public IDatabase Database { get { return _Redis.GetDatabase(m_DatabaseId); } }
    
            private ConnectionMultiplexer _Redis
            {
                get
                {
                    if (m_Connecting == true)
                    {
                        throw new RedisConnectionNotReadyException();
                    }
    
                    ConnectionMultiplexer redis = m_Redis;
                    if (redis == null)
                    {
                        throw new RedisConnectionNotReadyException();
                    }
    
                    return redis;
                }
            }
    
            private ConnectionMultiplexer _Sentinel
            {
                get
                {
                    if (m_Connecting == true)
                    {
                        throw new RedisConnectionNotReadyException("Sentinel connection not ready");
                    }
    
                    ConnectionMultiplexer sentinel = m_Sentinel;
                    if (sentinel == null)
                    {
                        throw new RedisConnectionNotReadyException("Sentinel connection not ready");
                    }
    
                    return sentinel;
                }
            }
    
            public void RegisterSubscription(string channel, Action<RedisChannel, RedisValue> handler, Int32 maxNoReceiveSeconds)
            {
                m_Subscriptions.Add(channel, new RedisSubscription
                {
                    Channel = channel,
                    Handler = handler,
                    MaxNoReceiveSeconds = maxNoReceiveSeconds,
                    LastUsed = DateTime.UtcNow,
                });
            }
    
            public void Connect()
            {
                _Connect(true);
            }
    
            private void _Connect(object state)
            {
                bool throwException = (bool)state;
    
                // if a reconnect is already being attempted, don't hang around waiting
                if (Monitor.TryEnter(m_ConnectionLocker) == false)
                {
                    return;
                }
    
                // we took the lock, notify everything we are connecting
                m_Connecting = true;
    
                try
                {
                    Stopwatch sw = Stopwatch.StartNew();
                    LoggerQueue.Debug(">>>>>> REDIS CONNECTING... >>>>>>");
    
                    // if this is a reconnect, make absolutely sure everything is cleaned up first
                    _KillTimers();
                    _KillRedisClient();
    
                    if (this.IsSentinel == true && m_Sentinel == null)
                    {
                        LoggerQueue.Debug(">>>>>> CONNECTING TO SENTINEL >>>>>> - " + sw.Elapsed);
    
                        // we'll be getting the redis servers from sentinel
                        ConfigurationOptions sentinelConnection = _CreateRedisConfiguration(CommandMap.Sentinel, null, m_SentinelServers);
                        m_Sentinel = ConnectionMultiplexer.Connect(sentinelConnection);
                        LoggerQueue.Debug(">>>>>> CONNECTED TO SENTINEL >>>>>> - " + sw.Elapsed);
    
                        _OutputConfigurationFromSentinel();
    
                        // get all the redis servers from sentinel and ignore any set by caller
                        m_RedisServers.Clear();
                        m_RedisServers.AddRange(_GetAllRedisServersFromSentinel());
    
                        if (m_RedisServers.Count == 0)
                        {
                            throw new RedisException("Sentinel found no redis servers");
                        }
                    }
    
                    LoggerQueue.Debug(">>>>>> CONNECTING TO REDIS >>>>>> - " + sw.Elapsed);
    
                    // try to connect to all redis servers
                    ConfigurationOptions connection = _CreateRedisConfiguration(CommandMap.Default, _SecureStringToString(m_Password), m_RedisServers);
                    m_Redis = ConnectionMultiplexer.Connect(connection);
                    LoggerQueue.Debug(">>>>>> CONNECTED TO REDIS >>>>>> - " + sw.Elapsed);
    
                    // register subscription channels
                    m_Subscriptions.ForEach(s =>
                    {
                        m_Redis.GetSubscriber().Subscribe(s.Key, (channel, value) => _SubscriptionHandler(channel, value));
                        s.Value.LastUsed = DateTime.UtcNow;
                    });
    
                    if (this.IsSentinel == true)
                    {
                        // check subscriptions have been sending messages
                        if (m_Subscriptions.Count > 0)
                        {
                            m_CheckSubscriptionsTimer = new Timer(_CheckSubscriptions, null, 30000, m_CheckSubscriptionsTime);
                        }
    
                        if (m_CheckWriteTime != null)
                        {
                            // check that we can write to redis
                            m_CheckWriteTimer = new Timer(_CheckWrite, null, 32000, m_CheckWriteTime.Value);
                        }
    
                        // monitor for connection status change to any redis servers
                        m_Redis.ConnectionFailed += _ConnectionFailure;
                        m_Redis.ConnectionRestored += _ConnectionRestored;
                    }
    
                    LoggerQueue.Debug(string.Format(">>>>>> ALL REDIS CONNECTED ({0}) >>>>>>", sw.Elapsed));
                }
                catch (Exception ex)
                {
                    LoggerQueue.Error(">>>>>> REDIS CONNECT FAILURE >>>>>>", ex);
    
                    if (throwException == true)
                    {
                        throw;
                    }
                    else
                    {
                        // internal reconnect, the reconnect has failed so might as well clean everything and try again
                        _KillTimers();
                        _KillRedisClient();
    
                        // faster than usual reconnect if failure
                        _ReconnectTimer(1000);
                    }
                }
                finally
                {
                    // finished connection attempt, notify everything and remove lock
                    m_Connecting = false;
                    Monitor.Exit(m_ConnectionLocker);
                }
            }
    
            private ConfigurationOptions _CreateRedisConfiguration(CommandMap commandMap, string password, List<RedisConnection> connections)
            {
                ConfigurationOptions connection = new ConfigurationOptions
                {
                    CommandMap = commandMap,
                    AbortOnConnectFail = true,
                    AllowAdmin = true,
                    ConnectTimeout = m_ConnectTimeout,
                    SyncTimeout = m_Timeout,
                    ServiceName = "master",
                    TieBreaker = string.Empty,
                    Password = password,
                };
    
                connections.ForEach(s =>
                {
                    connection.EndPoints.Add(s.Address, s.Port);
                });
    
                return connection;
            }
    
            private void _OutputConfigurationFromSentinel()
            {
                m_SentinelServers.ForEach(s =>
                {
                    try
                    {
                        IServer server = m_Sentinel.GetServer(s.Address, s.Port);
                        if (server.IsConnected == true)
                        {
                            try
                            {
                                IPEndPoint master = server.SentinelGetMasterAddressByName("master") as IPEndPoint;
                                var slaves = server.SentinelSlaves("master");
    
                                StringBuilder sb = new StringBuilder();
                                sb.Append(">>>>>> _OutputConfigurationFromSentinel Server ");
                                sb.Append(s.Address);
                                sb.Append(" thinks that master is ");
                                sb.Append(master);
                                sb.Append(" and slaves are ");
    
                                foreach (var slave in slaves)
                                {
                                    string name = slave.Where(i => i.Key == "name").Single().Value;
                                    bool up = slave.Where(i => i.Key == "flags").Single().Value.Contains("disconnected") == false;
    
                                    sb.Append(name);
                                    sb.Append("(");
                                    sb.Append(up == true ? "connected" : "down");
                                    sb.Append(") ");
                                }
                                sb.Append(">>>>>>");
                                LoggerQueue.Debug(sb.ToString());
                            }
                            catch (Exception ex)
                            {
                                LoggerQueue.Error(string.Format(">>>>>> _OutputConfigurationFromSentinel Could not get configuration from sentinel server ({0}) >>>>>>", s.Address), ex);
                            }
                        }
                        else
                        {
                            LoggerQueue.Error(string.Format(">>>>>> _OutputConfigurationFromSentinel Sentinel server {0} was not connected", s.Address));
                        }
                    }
                    catch (Exception ex)
                    {
                        LoggerQueue.Error(string.Format(">>>>>> _OutputConfigurationFromSentinel Could not get IServer from sentinel ({0}) >>>>>>", s.Address), ex);
                    }
                });
            }
    
            private RedisConnection[] _GetAllRedisServersFromSentinel()
            {
                // ask each sentinel server for its configuration
                List<RedisConnection> redisServers = new List<RedisConnection>();
                m_SentinelServers.ForEach(s =>
                {
                    try
                    {
                        IServer server = m_Sentinel.GetServer(s.Address, s.Port);
                        if (server.IsConnected == true)
                        {
                            try
                            {
                                // store master in list
                                IPEndPoint master = server.SentinelGetMasterAddressByName("master") as IPEndPoint;
                                redisServers.Add(new RedisConnection { Address = master.Address.ToString(), Port = master.Port });
    
                                var slaves = server.SentinelSlaves("master");
                                foreach (var slave in slaves)
                                {
                                    string address = slave.Where(i => i.Key == "ip").Single().Value;
                                    string port = slave.Where(i => i.Key == "port").Single().Value;
    
                                    redisServers.Add(new RedisConnection { Address = address, Port = Convert.ToInt32(port) });
                                }
                            }
                            catch (Exception ex)
                            {
                                LoggerQueue.Error(string.Format(">>>>>> _GetAllRedisServersFromSentinel Could not get redis servers from sentinel server ({0}) >>>>>>", s.Address), ex);
                            }
                        }
                        else
                        {
                            LoggerQueue.Error(string.Format(">>>>>> _GetAllRedisServersFromSentinel Sentinel server {0} was not connected", s.Address));
                        }
                    }
                    catch (Exception ex)
                    {
                        LoggerQueue.Error(string.Format(">>>>>> _GetAllRedisServersFromSentinel Could not get IServer from sentinel ({0}) >>>>>>", s.Address), ex);
                    }
                });
    
                return redisServers.Distinct().ToArray();
            }
    
            private IServer _GetRedisMasterFromSentinel()
            {
                // ask each sentinel server for its configuration
                foreach (RedisConnection sentinel in m_SentinelServers)
                {
                    IServer sentinelServer = _Sentinel.GetServer(sentinel.Address, sentinel.Port);
                    if (sentinelServer.IsConnected == true)
                    {
                        try
                        {
                            IPEndPoint master = sentinelServer.SentinelGetMasterAddressByName("master") as IPEndPoint;
                            return _Redis.GetServer(master);
                        }
                        catch (Exception ex)
                        {
                            LoggerQueue.Error(string.Format(">>>>>> Could not get redis master from sentinel server ({0}) >>>>>>", sentinel.Address), ex);
                        }
                    }
                }
    
                throw new InvalidOperationException("No sentinel server available to get master");
            }
    
            private void _ReconnectTimer(Nullable<Int32> reconnectMilliseconds)
            {
                try
                {
                    lock (m_ReconnectLocker)
                    {
                        if (m_ReconnectTimer != null)
                        {
                            m_ReconnectTimer.Dispose();
                            m_ReconnectTimer = null;
                        }
    
                        // since a reconnect will definately occur we can stop the check timers for now until reconnect succeeds (where they are recreated)
                        _KillTimers();
    
                        LoggerQueue.Warn(">>>>>> REDIS STARTING RECONNECT TIMER >>>>>>");
    
                        m_ReconnectTimer = new Timer(_Connect, false, reconnectMilliseconds.GetValueOrDefault(m_ReconnectTime), Timeout.Infinite);
                    }
                }
                catch (Exception ex)
                {
                    LoggerQueue.Error("Error during _ReconnectTimer", ex);
                }
            }
    
            private void _CheckSubscriptions(object state)
            {
                if (Monitor.TryEnter(m_ConnectionLocker, TimeSpan.FromSeconds(1)) == false)
                {
                    return;
                }
    
                try
                {
                    DateTime now = DateTime.UtcNow;
                    foreach (RedisSubscription subscription in m_Subscriptions.Values)
                    {
                        if ((now - subscription.LastUsed) > TimeSpan.FromSeconds(subscription.MaxNoReceiveSeconds))
                        {
                            try
                            {
                                EndPoint endpoint = m_Redis.GetSubscriber().IdentifyEndpoint(subscription.Channel);
                                EndPoint subscribedEndpoint = m_Redis.GetSubscriber().SubscribedEndpoint(subscription.Channel);
    
                                LoggerQueue.Warn(string.Format(">>>>>> REDIS Channel '{0}' has not been used for longer than {1}s, IsConnected: {2}, IsConnectedChannel: {3}, EndPoint: {4}, SubscribedEndPoint: {5}, reconnecting...", subscription.Channel, subscription.MaxNoReceiveSeconds, m_Redis.GetSubscriber().IsConnected(), m_Redis.GetSubscriber().IsConnected(subscription.Channel), endpoint != null ? endpoint.ToString() : "null", subscribedEndpoint != null ? subscribedEndpoint.ToString() : "null"));
                            }
                            catch (Exception ex)
                            {
                                LoggerQueue.Error(string.Format(">>>>>> REDIS Error logging out details of Channel '{0}' reconnect", subscription.Channel), ex);
                            }
    
                            _ReconnectTimer(null);
                            return;
                        }
                    }
                }
                catch (Exception ex)
                {
                    LoggerQueue.Error(">>>>>> REDIS Exception ERROR during _CheckSubscriptions", ex);
                }
                finally
                {
                    Monitor.Exit(m_ConnectionLocker);
                }
            }
    
            private void _CheckWrite(object state)
            {
                if (Monitor.TryEnter(m_ConnectionLocker, TimeSpan.FromSeconds(1)) == false)
                {
                    return;
                }
    
                try
                {
                    this.Database.HashSet(Environment.MachineName + "SmartoddsWriteCheck", m_CheckWriteGuid.ToString(), DateTime.UtcNow.Ticks);
                }
                catch (RedisConnectionNotReadyException)
                {
                    LoggerQueue.Warn(">>>>>> REDIS RedisConnectionNotReadyException ERROR DURING _CheckWrite");
                }
                catch (RedisServerException ex)
                {
                    LoggerQueue.Warn(">>>>>> REDIS RedisServerException ERROR DURING _CheckWrite, reconnecting... - " + ex.Message);
                    _ReconnectTimer(null);
                }
                catch (RedisConnectionException ex)
                {
                    LoggerQueue.Warn(">>>>>> REDIS RedisConnectionException ERROR DURING _CheckWrite, reconnecting... - " + ex.Message);
                    _ReconnectTimer(null);
                }
                catch (TimeoutException ex)
                {
                    LoggerQueue.Warn(">>>>>> REDIS TimeoutException ERROR DURING _CheckWrite - " + ex.Message);
                }
                catch (Exception ex)
                {
                    LoggerQueue.Error(">>>>>> REDIS Exception ERROR during _CheckWrite", ex);
                }
                finally
                {
                    Monitor.Exit(m_ConnectionLocker);
                }
            }
    
            private void _ConnectionFailure(object sender, ConnectionFailedEventArgs e)
            {
                LoggerQueue.Warn(string.Format(">>>>>> REDIS CONNECTION FAILURE, {0}, {1}, {2} >>>>>>", e.ConnectionType, e.EndPoint.ToString(), e.FailureType));
            }
    
            private void _ConnectionRestored(object sender, ConnectionFailedEventArgs e)
            {
                LoggerQueue.Warn(string.Format(">>>>>> REDIS CONNECTION RESTORED, {0}, {1}, {2} >>>>>>", e.ConnectionType, e.EndPoint.ToString(), e.FailureType));
            }
    
            private void _SubscriptionHandler(string channel, RedisValue value)
            {
                // get handler lookup
                RedisSubscription subscription = null;
                if (m_Subscriptions.TryGetValue(channel, out subscription) == false || subscription == null)
                {
                    return;
                }
    
                // update last used
                subscription.LastUsed = DateTime.UtcNow;
    
                // call handler
                subscription.Handler(channel, value);
            }
    
            public Int64 Publish(string channel, RedisValue message)
            {
                try
                {
                    return _Redis.GetSubscriber().Publish(channel, message);
                }
                catch (RedisConnectionNotReadyException)
                {
                    LoggerQueue.Error("REDIS RedisConnectionNotReadyException ERROR DURING Publish");
                    throw;
                }
                catch (RedisServerException ex)
                {
                    LoggerQueue.Error("REDIS RedisServerException ERROR DURING Publish - " + ex.Message);
                    throw;
                }
                catch (RedisConnectionException ex)
                {
                    LoggerQueue.Error("REDIS RedisConnectionException ERROR DURING Publish - " + ex.Message);
                    throw;
                }
                catch (TimeoutException ex)
                {
                    LoggerQueue.Error("REDIS TimeoutException ERROR DURING Publish - " + ex.Message);
                    throw;
                }
                catch (Exception ex)
                {
                    LoggerQueue.Error("REDIS Exception ERROR DURING Publish", ex);
                    throw;
                }
            }
    
            public bool LockTake(RedisKey key, RedisValue value, TimeSpan expiry)
            {
                return _Execute(() => this.Database.LockTake(key, value, expiry));
            }
    
            public bool LockExtend(RedisKey key, RedisValue value, TimeSpan extension)
            {
                return _Execute(() => this.Database.LockExtend(key, value, extension));
            }
    
            public bool LockRelease(RedisKey key, RedisValue value)
            {
                return _Execute(() => this.Database.LockRelease(key, value));
            }
    
            private void _Execute(Action action)
            {
                try
                {
                    action.Invoke();
                }
                catch (RedisServerException ex)
                {
                    LoggerQueue.Error("REDIS RedisServerException ERROR DURING _Execute - " + ex.Message);
                    throw;
                }
                catch (RedisConnectionException ex)
                {
                    LoggerQueue.Error("REDIS RedisConnectionException ERROR DURING _Execute - " + ex.Message);
                    throw;
                }
                catch (TimeoutException ex)
                {
                    LoggerQueue.Error("REDIS TimeoutException ERROR DURING _Execute - " + ex.Message);
                    throw;
                }
                catch (Exception ex)
                {
                    LoggerQueue.Error("REDIS Exception ERROR DURING _Execute", ex);
                    throw;
                }
            }
    
            private TResult _Execute<TResult>(Func<TResult> function)
            {
                try
                {
                    return function.Invoke();
                }
                catch (RedisServerException ex)
                {
                    LoggerQueue.Error("REDIS RedisServerException ERROR DURING _Execute - " + ex.Message);
                    throw;
                }
                catch (RedisConnectionException ex)
                {
                    LoggerQueue.Error("REDIS RedisConnectionException ERROR DURING _Execute - " + ex.Message);
                    throw;
                }
                catch (TimeoutException ex)
                {
                    LoggerQueue.Error("REDIS TimeoutException ERROR DURING _Execute - " + ex.Message);
                    throw;
                }
                catch (Exception ex)
                {
                    LoggerQueue.Error("REDIS ERROR DURING _Execute", ex);
                    throw;
                }
            }
    
            public string[] GetAllKeys(string pattern)
            {
                if (m_Sentinel != null)
                {
                    return _GetAnyRedisSlaveFromSentinel().Keys(m_DatabaseId, pattern).Select(k => (string)k).ToArray();
                }
                else
                {
                    return _Redis.GetServer(_Redis.GetEndPoints().First()).Keys(m_DatabaseId, pattern).Select(k => (string)k).ToArray();
                }
            }
    
            private void _KillSentinelClient()
            {
                try
                {
                    if (m_Sentinel != null)
                    {
                        LoggerQueue.Debug(">>>>>> KILLING SENTINEL CONNECTION >>>>>>");
    
                        ConnectionMultiplexer sentinel = m_Sentinel;
                        m_Sentinel = null;
    
                        sentinel.Close(false);
                        sentinel.Dispose();
                    }
                }
                catch (Exception ex)
                {
                    LoggerQueue.Error(">>>>>> Error during _KillSentinelClient", ex);
                }
            }
    
            private void _KillRedisClient()
            {
                try
                {
                    if (m_Redis != null)
                    {
                        Stopwatch sw = Stopwatch.StartNew();
                        LoggerQueue.Debug(">>>>>> KILLING REDIS CONNECTION >>>>>>");
    
                        ConnectionMultiplexer redis = m_Redis;
                        m_Redis = null;
    
                        if (this.IsSentinel == true)
                        {
                            redis.ConnectionFailed -= _ConnectionFailure;
                            redis.ConnectionRestored -= _ConnectionRestored;
                        }
    
                        redis.Close(false);
                        redis.Dispose();
    
                        LoggerQueue.Debug(">>>>>> KILLED REDIS CONNECTION >>>>>> " + sw.Elapsed);
                    }
                }
                catch (Exception ex)
                {
                    LoggerQueue.Error(">>>>>> Error during _KillRedisClient", ex);
                }
            }
    
            private void _KillClients()
            {
                lock (m_ConnectionLocker)
                {
                    _KillSentinelClient();
                    _KillRedisClient();
                }
            }
    
            private void _KillTimers()
            {
                if (m_CheckSubscriptionsTimer != null)
                {
                    m_CheckSubscriptionsTimer.Dispose();
                    m_CheckSubscriptionsTimer = null;
                }
                if (m_CheckWriteTimer != null)
                {
                    m_CheckWriteTimer.Dispose();
                    m_CheckWriteTimer = null;
                }
            }
    
            public void Dispose()
            {
                _KillClients();
                _KillTimers();
            }
        }
    }
    
  4. from https://stackoverflow.com/questions/25385075/redis-failover-with-stackexchange-sentinel-from-c-sharp by cc-by-sa and MIT license