参考文档:
https://blog.csdn.net/smj20170417/article/details/79928228
https://www.jianshu.com/p/0b3be884d2f5
Redis链接字符串可以提出来放到 Config文件当中:
"Connection_Redis" connectionString="127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,password=123456,abortConnect=false" />
当有多个Redis实例时,可以设置链接多个实例,中间用逗号分隔即可(比如:使用Redis集群)。
1 public class RedisCacheHelper 2 { 3 private readonly Logger _log = LogManager.GetCurrentClassLogger(); 4 5 ///6 /// 连接字符串 7 /// 8 private static string _connectionString; 9 10 /// 11 /// redis 连接对象 12 /// 13 private static IConnectionMultiplexer _connMultiplexer; 14 15 /// 16 /// 实例化对象 17 /// 18 private static RedisCacheHelper _cacheHelper; 19 /// 20 /// 实例 21 /// 22 private static RedisCacheHelper _instance; 23 24 /// 25 /// 锁 26 /// 27 private static readonly object Locker = new object(); 28 29 /// 30 /// 数据库 31 /// 32 private IDatabase _db; 33 34 35 /// 36 /// 默认链接实例 37 /// 38 private RedisCacheHelper() 39 { 40 _connectionString = ConfigurationManager.ConnectionStrings["Connection_Redis"].ConnectionString; 41 _connMultiplexer = ConnectionMultiplexer.Connect(_connectionString); 42 //添加注册事件 43 AddRegisterEvent(); 44 } 45 46 /// 47 /// 获取 Redis 连接对象 48 /// 49 private IConnectionMultiplexer Connnection 50 { 51 get 52 { 53 if (_connMultiplexer == null || !_connMultiplexer.IsConnected) 54 { 55 lock (Locker) 56 { 57 if (_connMultiplexer == null || !_connMultiplexer.IsConnected) 58 { 59 _connMultiplexer = ConnectionMultiplexer.Connect(_connectionString); 60 } 61 } 62 } 63 return _connMultiplexer; 64 } 65 } 66 67 /// 68 /// 获取指定db,默认不指定 69 /// 70 /// 71 /// 72 private IDatabase GetDatabase(int db = -1) 73 { 74 return Connnection.GetDatabase(db); 75 } 76 77 /// 78 /// 调用实例,通过该实例调用Redis 79 /// 80 public static RedisCacheHelper Instance 81 { 82 get 83 { 84 if (_cacheHelper != null) return _cacheHelper; 85 lock (Locker) 86 { 87 if (_cacheHelper != null) return _cacheHelper; 88 _cacheHelper = new RedisCacheHelper(); 89 } 90 return _cacheHelper; 91 } 92 } 93 94 #region 注册事件 95 96 /// 97 /// 添加注册事件 98 /// 99 private void AddRegisterEvent() 100 { 101 _connMultiplexer.ConnectionRestored += ConnMultiplexer_ConnectionRestored; 102 _connMultiplexer.ConnectionFailed += ConnMultiplexer_ConnectionFailed; 103 _connMultiplexer.ErrorMessage += ConnMultiplexer_ErrorMessage; 104 _connMultiplexer.ConfigurationChanged += ConnMultiplexer_ConfigurationChanged; 105 _connMultiplexer.HashSlotMoved += ConnMultiplexer_HashSlotMoved; 106 _connMultiplexer.InternalError += ConnMultiplexer_InternalError; 107 _connMultiplexer.ConfigurationChangedBroadcast += ConnMultiplexer_ConfigurationChangedBroadcast; 108 } 109 110 /// 111 /// 重新配置广播时(通常意味着主从同步更改) 112 /// 113 /// 114 /// 115 private void ConnMultiplexer_ConfigurationChangedBroadcast(object sender, EndPointEventArgs e) 116 { 117 _log.Info($"{nameof(ConnMultiplexer_ConfigurationChangedBroadcast)}: {e.EndPoint}"); 118 } 119 120 /// 121 /// 发生内部错误时(主要用于调试) 122 /// 123 /// 124 /// 125 private void ConnMultiplexer_InternalError(object sender, InternalErrorEventArgs e) 126 { 127 _log.Error($"{nameof(ConnMultiplexer_InternalError)}: {e.Exception}"); 128 } 129 130 /// 131 /// 更改集群时 132 /// 133 /// 134 /// 135 private void ConnMultiplexer_HashSlotMoved(object sender, HashSlotMovedEventArgs e) 136 { 137 _log.Info( 138 $"{nameof(ConnMultiplexer_HashSlotMoved)}: {nameof(e.OldEndPoint)}-{e.OldEndPoint} To {nameof(e.NewEndPoint)}-{e.NewEndPoint}"); 139 } 140 141 /// 142 /// 配置更改时 143 /// 144 /// 145 /// 146 private void ConnMultiplexer_ConfigurationChanged(object sender, EndPointEventArgs e) 147 { 148 _log.Info($"{nameof(ConnMultiplexer_ConfigurationChanged)}: {e.EndPoint}"); 149 } 150 151 /// 152 /// 发生错误时 153 /// 154 /// 155 /// 156 private void ConnMultiplexer_ErrorMessage(object sender, RedisErrorEventArgs e) 157 { 158 _log.Error($"{nameof(ConnMultiplexer_ErrorMessage)}: {e.Message}"); 159 } 160 161 /// 162 /// 物理连接失败时 163 /// 164 /// 165 /// 166 private void ConnMultiplexer_ConnectionFailed(object sender, ConnectionFailedEventArgs e) 167 { 168 _log.Fatal($"{nameof(ConnMultiplexer_ConnectionFailed)}: {e.Exception}"); 169 } 170 171 /// 172 /// 建立物理连接时 173 /// 174 /// 175 /// 176 private void ConnMultiplexer_ConnectionRestored(object sender, ConnectionFailedEventArgs e) 177 { 178 _log.Info($"{nameof(ConnMultiplexer_ConnectionRestored)}: {e.Exception}"); 179 } 180 181 #endregion 182 }
以上Redis链接就配置好了,使用方式如下:首先在把集群中的主从服务都开启,3主、3从
然后在代码中进行操作验证,向缓存中插入一条数据,然后循环读写数据,循环的时候手动任意关闭Redis服务当中的 1~2个 服务器
1 while (true) 2 { 3 //设置age 4 RedisCacheHelper.Instance.Set("age", "10", new TimeSpan(0, 5, 0)); 5 6 //获取age 7 var getage = RedisCacheHelper.Instance.Get("age"); 8 Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + ":" + getage); 9 10 Thread.Sleep(1000); // 等待1s 11 }
成功将缓存存入到服务器当中,运行效果如下:
此时查看Redis集群,发现主从实例都有数据了,接下来我们把其中任意1个 Redis实例关掉 (上边运行的循环代码程序不要关闭)
发现程序正常运行:
但是上边的步骤,我经过多次测试,在任意关闭某个实例的时候,偶尔会报如下错误:集群挂了
我们把写的方法注释掉,只保留读取的代码,同样会偶尔报异常。
但是大部分情况下,关掉其中一个实例,程序都正常运行: