Untitled
unknown
plain_text
2 years ago
6.5 kB
3
Indexable
#region Redis Base string GetGUID() { return String.Concat(Guid.NewGuid().ToString("N").Select(c => (char)(c + 17))); } private Lazy<ConnectionMultiplexer> lazyConnection = CreateConnection(); public ConnectionMultiplexer Connection { get { return lazyConnection.Value; } } private static Lazy<ConnectionMultiplexer> CreateConnection() { return new Lazy<ConnectionMultiplexer>(() => { string cacheConnection = CS; return ConnectionMultiplexer.Connect(cacheConnection); }); } private long lastReconnectTicks = DateTimeOffset.MinValue.UtcTicks; private DateTimeOffset firstErrorTime = DateTimeOffset.MinValue; private DateTimeOffset previousErrorTime = DateTimeOffset.MinValue; private readonly object reconnectLock = new object(); // In general, let StackExchange.Redis handle most reconnects, // so limit the frequency of how often ForceReconnect() will // actually reconnect. public TimeSpan ReconnectMinFrequency => TimeSpan.FromSeconds(60); // If errors continue for longer than the below threshold, then the // multiplexer seems to not be reconnecting, so ForceReconnect() will // re-create the multiplexer. public TimeSpan ReconnectErrorThreshold => TimeSpan.FromSeconds(30); public int RetryMaxAttempts => 5; private void CloseConnection(Lazy<ConnectionMultiplexer> oldConnection) { if (oldConnection == null) return; try { oldConnection.Value.Close(); } catch (Exception) { // Example error condition: if accessing oldConnection.Value causes a connection attempt and that fails. } } /// <summary> /// Force a new ConnectionMultiplexer to be created. /// NOTES: /// 1. Users of the ConnectionMultiplexer MUST handle ObjectDisposedExceptions, which can now happen as a result of calling ForceReconnect(). /// 2. Don't call ForceReconnect for Timeouts, just for RedisConnectionExceptions or SocketExceptions. /// 3. Call this method every time you see a connection exception. The code will: /// a. wait to reconnect for at least the "ReconnectErrorThreshold" time of repeated errors before actually reconnecting /// b. not reconnect more frequently than configured in "ReconnectMinFrequency" /// </summary> public void ForceReconnect() { var utcNow = DateTimeOffset.UtcNow; long previousTicks = Interlocked.Read(ref lastReconnectTicks); var previousReconnectTime = new DateTimeOffset(previousTicks, TimeSpan.Zero); TimeSpan elapsedSinceLastReconnect = utcNow - previousReconnectTime; // If multiple threads call ForceReconnect at the same time, we only want to honor one of them. if (elapsedSinceLastReconnect < ReconnectMinFrequency) return; lock (reconnectLock) { utcNow = DateTimeOffset.UtcNow; elapsedSinceLastReconnect = utcNow - previousReconnectTime; if (firstErrorTime == DateTimeOffset.MinValue) { // We haven't seen an error since last reconnect, so set initial values. firstErrorTime = utcNow; previousErrorTime = utcNow; return; } if (elapsedSinceLastReconnect < ReconnectMinFrequency) return; // Some other thread made it through the check and the lock, so nothing to do. TimeSpan elapsedSinceFirstError = utcNow - firstErrorTime; TimeSpan elapsedSinceMostRecentError = utcNow - previousErrorTime; bool shouldReconnect = elapsedSinceFirstError >= ReconnectErrorThreshold // Make sure we gave the multiplexer enough time to reconnect on its own if it could. && elapsedSinceMostRecentError <= ReconnectErrorThreshold; // Make sure we aren't working on stale data (e.g. if there was a gap in errors, don't reconnect yet). // Update the previousErrorTime timestamp to be now (e.g. this reconnect request). previousErrorTime = utcNow; if (!shouldReconnect) return; firstErrorTime = DateTimeOffset.MinValue; previousErrorTime = DateTimeOffset.MinValue; Lazy<ConnectionMultiplexer> oldConnection = lazyConnection; CloseConnection(oldConnection); lazyConnection = CreateConnection(); Interlocked.Exchange(ref lastReconnectTicks, utcNow.UtcTicks); } } // In real applications, consider using a framework such as // Polly to make it easier to customize the retry approach. private T BasicRetry<T>(Func<T> func) { int reconnectRetry = 0; int disposedRetry = 0; while (true) { try { return func(); } catch (Exception ex) when (ex is RedisConnectionException || ex is SocketException) { reconnectRetry++; if (reconnectRetry > RetryMaxAttempts) throw; ForceReconnect(); } catch (ObjectDisposedException) { disposedRetry++; if (disposedRetry > RetryMaxAttempts) throw; } } } public IDatabase GetDatabase() { return BasicRetry(() => Connection.GetDatabase()); } public System.Net.EndPoint[] GetEndPoints() { return BasicRetry(() => Connection.GetEndPoints()); } public IServer GetServer(string host, int port) { return BasicRetry(() => Connection.GetServer(host, port)); }
Editor is loading...