public SentinelAdapter(RedisClient topOwner, ConnectionStringBuilder sentinelConnectionString, string[] sentinels, bool rw_splitting) { UseType = UseType.Sentinel; TopOwner = topOwner; _connectionString = sentinelConnectionString; _sentinels = new LinkedList <ConnectionStringBuilder>(sentinels?.Select(a => { var csb = ConnectionStringBuilder.Parse(a); csb.Host = csb.Host.ToLower(); return(csb); }).GroupBy(a => a.Host, a => a).Select(a => a.First()) ?? new ConnectionStringBuilder[0]); _rw_splitting = rw_splitting; _is_single = !_rw_splitting && sentinelConnectionString.MaxPoolSize == 1; if (_sentinels.Any() == false) { throw new ArgumentNullException(nameof(sentinels)); } _ib = new IdleBus <RedisClientPool>(TimeSpan.FromMinutes(10)); ResetSentinel(); }
public static ConnectionStringBuilder Parse(string connectionString) { var ret = new ConnectionStringBuilder(); if (string.IsNullOrEmpty(connectionString)) return ret; //支持密码中带有逗号,将原有 split(',') 改成以下处理方式 var vs = Regex.Split(connectionString, @"\,([\w \t\r\n]+)=", RegexOptions.Multiline); ret.Host = vs[0].Trim(); for (var a = 1; a < vs.Length; a += 2) { var kv = new[] { Regex.Replace(vs[a].ToLower().Trim(), @"[ \t\r\n]", ""), vs[a + 1] }; switch (kv[0]) { case "ssl": if (kv.Length > 1 && kv[1].ToLower().Trim() == "true") ret.Ssl = true; break; case "protocol": if (kv.Length > 1 && kv[1].ToUpper().Trim() == "RESP3") ret.Protocol = RedisProtocol.RESP3; break; case "userid": case "user": if (kv.Length > 1) ret.User = kv[1].Trim(); break; case "password": if (kv.Length > 1) ret.Password = kv[1]; break; case "database": case "defaultdatabase": if (kv.Length > 1 && int.TryParse(kv[1].Trim(), out var database) && database > 0) ret.Database = database; break; case "prefix": if (kv.Length > 1) ret.Prefix = kv[1].Trim(); break; case "name": case "clientname": if (kv.Length > 1) ret.ClientName = kv[1].Trim(); break; case "encoding": if (kv.Length > 1) ret.Encoding = Encoding.GetEncoding(kv[1].Trim()); break; case "idletimeout": if (kv.Length > 1 && long.TryParse(kv[1].Trim(), out var idleTimeout) && idleTimeout > 0) ret.IdleTimeout = TimeSpan.FromMilliseconds(idleTimeout); break; case "connecttimeout": if (kv.Length > 1 && long.TryParse(kv[1].Trim(), out var connectTimeout) && connectTimeout > 0) ret.ConnectTimeout = TimeSpan.FromMilliseconds(connectTimeout); break; case "receivetimeout": if (kv.Length > 1 && long.TryParse(kv[1].Trim(), out var receiveTimeout) && receiveTimeout > 0) ret.ReceiveTimeout = TimeSpan.FromMilliseconds(receiveTimeout); break; case "sendtimeout": if (kv.Length > 1 && long.TryParse(kv[1].Trim(), out var sendTimeout) && sendTimeout > 0) ret.SendTimeout = TimeSpan.FromMilliseconds(sendTimeout); break; case "poolsize": case "maxpoolsize": if (kv.Length > 1 && int.TryParse(kv[1].Trim(), out var maxPoolSize) && maxPoolSize > 0) ret.MaxPoolSize = maxPoolSize; break; case "minpoolsize": if (kv.Length > 1 && int.TryParse(kv[1].Trim(), out var minPoolSize) && minPoolSize > 0) ret.MinPoolSize = minPoolSize; break; } } return ret; }
public PoolingAdapter(RedisClient topOwner, ConnectionStringBuilder connectionString, params ConnectionStringBuilder[] slaveConnectionStrings) { UseType = UseType.Pooling; TopOwner = topOwner; _masterHost = connectionString.Host; _rw_splitting = slaveConnectionStrings?.Any() == true; _is_single = !_rw_splitting && connectionString.MaxPoolSize == 1; _ib = new IdleBus <RedisClientPool>(TimeSpan.FromMinutes(10)); _ib.Register(_masterHost, () => new RedisClientPool(connectionString, null, TopOwner)); if (_rw_splitting) { foreach (var slave in slaveConnectionStrings) { _ib.TryRegister($"slave_{slave.Host}", () => new RedisClientPool(slave, null, TopOwner)); } } #if isasync _asyncManager = new AsyncRedisSocket.Manager(this); #endif }
/// <summary> /// Sentinel RedisClient /// </summary> public RedisClient(ConnectionStringBuilder sentinelConnectionString, string[] sentinels, bool rw_splitting) { Adapter = new SentinelAdapter(this, sentinelConnectionString, sentinels, rw_splitting); Prefix = sentinelConnectionString.Prefix; }
/// <summary> /// Pooling RedisClient /// </summary> public RedisClient(ConnectionStringBuilder connectionString, params ConnectionStringBuilder[] slaveConnectionStrings) { Adapter = new PoolingAdapter(this, connectionString, slaveConnectionStrings); Prefix = connectionString.Prefix; }
internal void ResetSentinel() { if (ResetSentinelFlag != 0) { return; } if (Interlocked.Increment(ref ResetSentinelFlag) != 1) { Interlocked.Decrement(ref ResetSentinelFlag); return; } string masterhostEnd = _masterHost; var allkeys = _ib.GetKeys().ToList(); for (int i = 0; i < _sentinels.Count; i++) { if (i > 0) { var first = _sentinels.First; _sentinels.RemoveFirst(); _sentinels.AddLast(first.Value); } try { using (var sentinelcli = new RedisSentinelClient(_sentinels.First.Value)) { var masterhost = sentinelcli.GetMasterAddrByName(_connectionString.Host); var masterConnectionString = localTestHost(masterhost, RoleType.Master); if (masterConnectionString == null) { continue; } masterhostEnd = masterhost; if (_rw_splitting) { foreach (var slave in sentinelcli.Salves(_connectionString.Host)) { ConnectionStringBuilder slaveConnectionString = localTestHost($"{slave.ip}:{slave.port}", RoleType.Slave); if (slaveConnectionString == null) { continue; } } } foreach (var sentinel in sentinelcli.Sentinels(_connectionString.Host)) { var remoteSentinelHost = $"{sentinel.ip}:{sentinel.port}"; if (_sentinels.Contains(remoteSentinelHost)) { continue; } _sentinels.AddLast(remoteSentinelHost); } } break; } catch { } } foreach (var spkey in allkeys) { _ib.TryRemove(spkey, true); } Interlocked.Exchange(ref _masterHost, masterhostEnd); Interlocked.Decrement(ref ResetSentinelFlag); ConnectionStringBuilder localTestHost(string host, RoleType role) { ConnectionStringBuilder connectionString = _connectionString.ToString(); connectionString.Host = host; connectionString.MinPoolSize = 1; connectionString.MaxPoolSize = 1; using (var cli = new RedisClient(connectionString)) { if (cli.Role().role != role) { return(null); } if (role == RoleType.Master) { //test set/get } } connectionString.MinPoolSize = connectionString.MinPoolSize; connectionString.MaxPoolSize = connectionString.MaxPoolSize; _ib.TryRegister(host, () => new RedisClientPool(connectionString, null, TopOwner)); allkeys.Remove(host); return(connectionString); } }
public override TValue AdapterCall <TValue>(CommandPacket cmd, Func <RedisResult, TValue> parse) { if (cmd._keyIndexes.Count > 1) //Multiple key slot values not equal { switch (cmd._command) { case "DEL": case "UNLINK": return(cmd._keyIndexes.Select((_, idx) => AdapterCall(cmd._command.InputKey(cmd.GetKey(idx)), parse)).Sum(a => a.ConvertTo <long>()).ConvertTo <TValue>()); case "MSET": cmd._keyIndexes.ForEach(idx => AdapterCall(cmd._command.InputKey(cmd._input[idx].ToInvariantCultureToString()).InputRaw(cmd._input[idx + 1]), parse)); return(default); case "MGET": return(cmd._keyIndexes.Select((_, idx) => { var rt = AdapterCall(cmd._command.InputKey(cmd.GetKey(idx)), parse); return rt.ConvertTo <object[]>().FirstOrDefault(); }).ToArray().ConvertTo <TValue>()); case "PFCOUNT": return(cmd._keyIndexes.Select((_, idx) => AdapterCall(cmd._command.InputKey(cmd.GetKey(idx)), parse)).Sum(a => a.ConvertTo <long>()).ConvertTo <TValue>()); } } return(TopOwner.LogCall(cmd, () => { RedisResult rt = null; RedisClientPool pool = null; var protocolRetry = false; using (var rds = GetRedisSocket(cmd)) { pool = (rds as DefaultRedisSocket.TempProxyRedisSocket)._pool; try { if (cmd._clusterMovedAsking) { cmd._clusterMovedAsking = false; var askingCmd = "ASKING".SubCommand(null).FlagReadbytes(false); rds.Write(askingCmd); rds.Read(askingCmd); } rds.Write(cmd); rt = rds.Read(cmd); } catch (ProtocolViolationException) { rds.ReleaseSocket(); cmd._protocolErrorTryCount++; if (cmd._protocolErrorTryCount <= pool._policy._connectionStringBuilder.Retry) { protocolRetry = true; } else { if (cmd.IsReadOnlyCommand() == false || cmd._protocolErrorTryCount > 1) { throw; } protocolRetry = true; } } catch (Exception ex) { if (pool?.SetUnavailable(ex) == true) { } throw; } } if (protocolRetry) { return AdapterCall(cmd, parse); } if (rt.IsError && pool != null) { var moved = ClusterMoved.ParseSimpleError(rt.SimpleError); if (moved != null && cmd._clusterMovedTryCount < 3) { cmd._clusterMovedTryCount++; if (moved.endpoint.StartsWith("127.0.0.1")) { moved.endpoint = $"{DefaultRedisSocket.SplitHost(pool._policy._connectionStringBuilder.Host).Key}:{moved.endpoint.Substring(10)}"; } else if (moved.endpoint.StartsWith("localhost", StringComparison.CurrentCultureIgnoreCase)) { moved.endpoint = $"{DefaultRedisSocket.SplitHost(pool._policy._connectionStringBuilder.Host).Key}:{moved.endpoint.Substring(10)}"; } ConnectionStringBuilder connectionString = pool._policy._connectionStringBuilder.ToString(); connectionString.Host = moved.endpoint; RegisterClusterNode(connectionString); if (moved.ismoved) { _slotCache.AddOrUpdate(moved.slot, connectionString.Host, (k1, v1) => connectionString.Host); } if (moved.isask) { cmd._clusterMovedAsking = true; } TopOwner.OnNotice(null, new NoticeEventArgs(NoticeType.Info, null, $"{(cmd.WriteTarget ?? "Not connected").PadRight(21)} > {cmd}\r\n{rt.SimpleError} ", null)); return AdapterCall(cmd, parse); } } return parse(rt); })); }
//closure connectionString void RegisterClusterNode(ConnectionStringBuilder connectionString) { _ib.TryRegister(connectionString.Host, () => new RedisClientPool(connectionString, null, TopOwner)); }
void RefershClusterNodes() { foreach (var testConnection in _clusterConnectionStrings) { RegisterClusterNode(testConnection); //尝试求出其他节点,并缓存slot try { var cnodes = AdapterCall <string>("CLUSTER".SubCommand("NODES"), rt => rt.ThrowOrValue <string>()).Split('\n'); foreach (var cnode in cnodes) { if (string.IsNullOrEmpty(cnode)) { continue; } var dt = cnode.Trim().Split(' '); if (dt.Length < 9) { continue; } if (!dt[2].StartsWith("master") && !dt[2].EndsWith("master")) { continue; } if (dt[7] != "connected") { continue; } var endpoint = dt[1]; var at40 = endpoint.IndexOf('@'); if (at40 != -1) { endpoint = endpoint.Remove(at40); } if (endpoint.StartsWith("127.0.0.1")) { endpoint = $"{DefaultRedisSocket.SplitHost(testConnection.Host).Key}:{endpoint.Substring(10)}"; } else if (endpoint.StartsWith("localhost", StringComparison.CurrentCultureIgnoreCase)) { endpoint = $"{DefaultRedisSocket.SplitHost(testConnection.Host).Key}:{endpoint.Substring(10)}"; } ConnectionStringBuilder connectionString = testConnection.ToString(); connectionString.Host = endpoint; RegisterClusterNode(connectionString); for (var slotIndex = 8; slotIndex < dt.Length; slotIndex++) { var slots = dt[slotIndex].Split('-'); if (ushort.TryParse(slots[0], out var tryslotStart) && ushort.TryParse(slots[1], out var tryslotEnd)) { for (var slot = tryslotStart; slot <= tryslotEnd; slot++) { _slotCache.AddOrUpdate(slot, connectionString.Host, (k1, v1) => connectionString.Host); } } } } break; } catch { _ib.TryRemove(testConnection.Host, true); } } if (_ib.GetKeys().Length == 0) { throw new RedisClientException($"All \"clusterConnectionStrings\" failed to connect"); } }
public override TValue AdapterCall <TValue>(CommandPacket cmd, Func <RedisResult, TValue> parse) { return(TopOwner.LogCall(cmd, () => { RedisResult rt = null; RedisClientPool pool = null; using (var rds = GetRedisSocket(cmd)) { pool = (rds as DefaultRedisSocket.TempProxyRedisSocket)._pool; try { if (cmd._clusterMovedAsking) { cmd._clusterMovedAsking = false; rds.Write("ASKING"); rds.Read(false); } rds.Write(cmd); rt = rds.Read(cmd._flagReadbytes); } catch (Exception ex) { if (pool?.SetUnavailable(ex) == true) { } throw ex; } } if (rt.IsError && pool != null) { var moved = ClusterMoved.ParseSimpleError(rt.SimpleError); if (moved != null && cmd._clusterMovedTryCount < 3) { cmd._clusterMovedTryCount++; if (moved.endpoint.StartsWith("127.0.0.1")) { moved.endpoint = $"{DefaultRedisSocket.SplitHost(pool._policy._connectionStringBuilder.Host).Key}:{moved.endpoint.Substring(10)}"; } else if (moved.endpoint.StartsWith("localhost", StringComparison.CurrentCultureIgnoreCase)) { moved.endpoint = $"{DefaultRedisSocket.SplitHost(pool._policy._connectionStringBuilder.Host).Key}:{moved.endpoint.Substring(10)}"; } ConnectionStringBuilder connectionString = pool._policy._connectionStringBuilder.ToString(); connectionString.Host = moved.endpoint; RegisterClusterNode(connectionString); if (moved.ismoved) { _slotCache.AddOrUpdate(moved.slot, connectionString.Host, (k1, v1) => connectionString.Host); } if (moved.isask) { cmd._clusterMovedAsking = true; } TopOwner.OnNotice(new NoticeEventArgs(NoticeType.Info, null, $"{(cmd.WriteHost ?? "Not connected")} > {cmd}\r\n{rt.SimpleError} ", null)); return AdapterCall(cmd, parse); } } rt.IsErrorThrow = TopOwner._isThrowRedisSimpleError; return parse(rt); })); }