前兩天朋友問我,有沒有使用過StackExchange.Redis,問我要個封裝類,由於之前都是使用ServiceStack.Redis,由於ServiceStack.Redis v4版本後是收費版的,所以現在也很有公司都在使用StackExchange.Redis而拋棄ServiceStack.R ...
前兩天朋友問我,有沒有使用過StackExchange.Redis,問我要個封裝類,由於之前都是使用ServiceStack.Redis,由於ServiceStack.Redis v4版本後是收費版的,所以現在也很有公司都在使用StackExchange.Redis而拋棄ServiceStack.Redis了。其實個人覺得,兩個驅動都不錯,只是由於ServiceStack.Redis收費導致目前很多公司都是基於V3版本的使用,也有人說V3版本有很多Bug,沒有維護和升級,不過至少目前我是沒發現Bug。
不過ServiceStack.Redis同StackExchange.Redis比較,拋開收費的來說,確認比StackExchange.Redis 更有優勢。StackExchange.Redis文檔很少,更不要說國內的文檔了,連github上面對應的介紹文檔都是很片面,這點我真的覺得StackExchange.Redis的作者至少要完善下文檔,很多都是要看源碼的例子才有。網上對StackExchange.Redis的使用例子也比ServiceStack.Redis少得多,不是說沒人用,只是我查來查去,大部分都是基於String類型的數據進行使用的封裝類,對於List,SortedSet,Hash的封裝操作都很少,基本都是東寫一點,西寫一點,很難找到完整的。在參考了一些文章和源碼後,這裡提供一個自己封裝的類,基本提供對於各種類型的使用封裝,提供給大家學習使用,如果有哪裡寫的不好的,大家也可以互相交流。
ConnectionMultiplexer 封裝
首先是 ConnectionMultiplexer 的封裝,ConnectionMultiplexer對象是StackExchange.Redis最中樞的對象。這個類的實例需要被整個應用程式域共用和重用的,所以不需要在每個操作中不停的創建該對象的實例,一般都是使用單例來創建和存放這個對象,這個在官網上也有說明。
1 /// <summary> 2 /// ConnectionMultiplexer對象管理幫助類 3 /// </summary> 4 public static class RedisConnectionHelp 5 { 6 //系統自定義Key首碼 7 public static readonly string SysCustomKey = ConfigurationManager.AppSettings["redisKey"] ?? ""; 8 9 //"127.0.0.1:6379,allowadmin=true 10 private static readonly string RedisConnectionString = ConfigurationManager.ConnectionStrings["RedisExchangeHosts"].ConnectionString; 11 12 private static readonly object Locker = new object(); 13 private static ConnectionMultiplexer _instance; 14 private static readonly ConcurrentDictionary<string, ConnectionMultiplexer> ConnectionCache = new ConcurrentDictionary<string, ConnectionMultiplexer>(); 15 16 /// <summary> 17 /// 單例獲取 18 /// </summary> 19 public static ConnectionMultiplexer Instance 20 { 21 get 22 { 23 if (_instance == null) 24 { 25 lock (Locker) 26 { 27 if (_instance == null || !_instance.IsConnected) 28 { 29 _instance = GetManager(); 30 } 31 } 32 } 33 return _instance; 34 } 35 } 36 37 /// <summary> 38 /// 緩存獲取 39 /// </summary> 40 /// <param name="connectionString"></param> 41 /// <returns></returns> 42 public static ConnectionMultiplexer GetConnectionMultiplexer(string connectionString) 43 { 44 if (!ConnectionCache.ContainsKey(connectionString)) 45 { 46 ConnectionCache[connectionString] = GetManager(connectionString); 47 } 48 return ConnectionCache[connectionString]; 49 } 50 51 private static ConnectionMultiplexer GetManager(string connectionString = null) 52 { 53 connectionString = connectionString ?? RedisConnectionString; 54 var connect = ConnectionMultiplexer.Connect(connectionString); 55 56 //註冊如下事件 57 connect.ConnectionFailed += MuxerConnectionFailed; 58 connect.ConnectionRestored += MuxerConnectionRestored; 59 connect.ErrorMessage += MuxerErrorMessage; 60 connect.ConfigurationChanged += MuxerConfigurationChanged; 61 connect.HashSlotMoved += MuxerHashSlotMoved; 62 connect.InternalError += MuxerInternalError; 63 64 return connect; 65 } 66 67 #region 事件 68 69 /// <summary> 70 /// 配置更改時 71 /// </summary> 72 /// <param name="sender"></param> 73 /// <param name="e"></param> 74 private static void MuxerConfigurationChanged(object sender, EndPointEventArgs e) 75 { 76 Console.WriteLine("Configuration changed: " + e.EndPoint); 77 } 78 79 /// <summary> 80 /// 發生錯誤時 81 /// </summary> 82 /// <param name="sender"></param> 83 /// <param name="e"></param> 84 private static void MuxerErrorMessage(object sender, RedisErrorEventArgs e) 85 { 86 Console.WriteLine("ErrorMessage: " + e.Message); 87 } 88 89 /// <summary> 90 /// 重新建立連接之前的錯誤 91 /// </summary> 92 /// <param name="sender"></param> 93 /// <param name="e"></param> 94 private static void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e) 95 { 96 Console.WriteLine("ConnectionRestored: " + e.EndPoint); 97 } 98 99 /// <summary> 100 /// 連接失敗 , 如果重新連接成功你將不會收到這個通知 101 /// </summary> 102 /// <param name="sender"></param> 103 /// <param name="e"></param> 104 private static void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e) 105 { 106 Console.WriteLine("重新連接:Endpoint failed: " + e.EndPoint + ", " + e.FailureType + (e.Exception == null ? "" : (", " + e.Exception.Message))); 107 } 108 109 /// <summary> 110 /// 更改集群 111 /// </summary> 112 /// <param name="sender"></param> 113 /// <param name="e"></param> 114 private static void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e) 115 { 116 Console.WriteLine("HashSlotMoved:NewEndPoint" + e.NewEndPoint + ", OldEndPoint" + e.OldEndPoint); 117 } 118 119 /// <summary> 120 /// redis類庫錯誤 121 /// </summary> 122 /// <param name="sender"></param> 123 /// <param name="e"></param> 124 private static void MuxerInternalError(object sender, InternalErrorEventArgs e) 125 { 126 Console.WriteLine("InternalError:Message" + e.Exception.Message); 127 } 128 129 #endregion 事件 130 }ConnectionMultiplexer幫助類
RedisHelper 通用操作類封
1 public class RedisHelper 2 { 3 private int DbNum { get; } 4 private readonly ConnectionMultiplexer _conn; 5 public string CustomKey; 6 7 #region 構造函數 8 9 public RedisHelper(int dbNum = 0) 10 : this(dbNum, null) 11 { 12 } 13 14 public RedisHelper(int dbNum, string readWriteHosts) 15 { 16 DbNum = dbNum; 17 _conn = 18 string.IsNullOrWhiteSpace(readWriteHosts) ? 19 RedisConnectionHelp.Instance : 20 RedisConnectionHelp.GetConnectionMultiplexer(readWriteHosts); 21 } 22 23 #region 輔助方法 24 25 private string AddSysCustomKey(string oldKey) 26 { 27 var prefixKey = CustomKey ?? RedisConnectionHelp.SysCustomKey; 28 return prefixKey + oldKey; 29 } 30 31 private T Do<T>(Func<IDatabase, T> func) 32 { 33 var database = _conn.GetDatabase(DbNum); 34 return func(database); 35 } 36 37 private string ConvertJson<T>(T value) 38 { 39 string result = value is string ? value.ToString() : JsonConvert.SerializeObject(value); 40 return result; 41 } 42 43 private T ConvertObj<T>(RedisValue value) 44 { 45 return JsonConvert.DeserializeObject<T>(value); 46 } 47 48 private List<T> ConvetList<T>(RedisValue[] values) 49 { 50 List<T> result = new List<T>(); 51 foreach (var item in values) 52 { 53 var model = ConvertObj<T>(item); 54 result.Add(model); 55 } 56 return result; 57 } 58 59 private RedisKey[] ConvertRedisKeys(List<string> redisKeys) 60 { 61 return redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray(); 62 } 63 64 #endregion 輔助方法 65 66 #endregion 構造函數 67 }RedisHelper
其中CustomKey用來表示系統首碼,AddSysCustomKey方法對每個key都進行首碼的添加處理,這裡推薦大家在命名redis的key的時候最好的加上首碼,並且使用 :來分割首碼 ,這裡在使用可視化工具查看的時候就比較好區分,比如我的的首碼是 Demo:test:(一般是 系統名:業務名:),然後你查看的時候你會發現整齊,好區分了很多
String類型的封裝
1 #region String 2 3 #region 同步方法 4 5 /// <summary> 6 /// 保存單個key value 7 /// </summary> 8 /// <param name="key">Redis Key</param> 9 /// <param name="value">保存的值</param> 10 /// <param name="expiry">過期時間</param> 11 /// <returns></returns> 12 public bool StringSet(string key, string value, TimeSpan? expiry = default(TimeSpan?)) 13 { 14 key = AddSysCustomKey(key); 15 return Do(db => db.StringSet(key, value, expiry)); 16 } 17 18 /// <summary> 19 /// 保存多個key value 20 /// </summary> 21 /// <param name="keyValues">鍵值對</param> 22 /// <returns></returns> 23 public bool StringSet(List<KeyValuePair<RedisKey, RedisValue>> keyValues) 24 { 25 List<KeyValuePair<RedisKey, RedisValue>> newkeyValues = 26 keyValues.Select(p => new KeyValuePair<RedisKey, RedisValue>(AddSysCustomKey(p.Key), p.Value)).ToList(); 27 return Do(db => db.StringSet(newkeyValues.ToArray())); 28 } 29 30 /// <summary> 31 /// 保存一個對象 32 /// </summary> 33 /// <typeparam name="T"></typeparam> 34 /// <param name="key"></param> 35 /// <param name="obj"></param> 36 /// <param name="expiry"></param> 37 /// <returns></returns> 38 public bool StringSet<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?)) 39 { 40 key = AddSysCustomKey(key); 41 string json = ConvertJson(obj); 42 return Do(db => db.StringSet(key, json, expiry)); 43 } 44 45 /// <summary> 46 /// 獲取單個key的值 47 /// </summary> 48 /// <param name="key">Redis Key</param> 49 /// <returns></returns> 50 public string StringGet(string key) 51 { 52 key = AddSysCustomKey(key); 53 return Do(db => db.StringGet(key)); 54 } 55 56 /// <summary> 57 /// 獲取多個Key 58 /// </summary> 59 /// <param name="listKey">Redis Key集合</param> 60 /// <returns></returns> 61 public RedisValue[] StringGet(List<string> listKey) 62 { 63 List<string> newKeys = listKey.Select(AddSysCustomKey).ToList(); 64 return Do(db => db.StringGet(ConvertRedisKeys(newKeys))); 65 } 66 67 /// <summary> 68 /// 獲取一個key的對象 69 /// </summary> 70 /// <typeparam name="T"></typeparam> 71 /// <param name="key"></param> 72 /// <returns></returns> 73 public T StringGet<T>(string key) 74 { 75 key = AddSysCustomKey(key); 76 return Do(db => ConvertObj<T>(db.StringGet(key))); 77 } 78 79 /// <summary> 80 /// 為數字增長val 81 /// </summary> 82 /// <param name="key"></param> 83 /// <param name="val">可以為負</param> 84 /// <returns>增長後的值</returns> 85 public double StringIncrement(string key, double val = 1) 86 { 87 key = AddSysCustomKey(key); 88 return Do(db => db.StringIncrement(key, val)); 89 } 90 91 /// <summary> 92 /// 為數字減少val 93 /// </summary> 94 /// <param name="key"></param> 95 /// <param name="val">可以為負</param> 96 /// <returns>減少後的值</returns> 97 public double StringDecrement(string key, double val = 1) 98 { 99 key = AddSysCustomKey(key); 100 return Do(db => db.StringDecrement(key, val)); 101 } 102 103 #endregion 同步方法 104 105 #region 非同步方法 106 107 /// <summary> 108 /// 保存單個key value 109 /// </summary> 110 /// <param name="key">Redis Key</param> 111 /// <param name="value">保存的值</param> 112 /// <param name="expiry">過期時間</param> 113 /// <returns></returns> 114 public async Task<bool> StringSetAsync(string key, string value, TimeSpan? expiry = default(TimeSpan?)) 115 { 116 key = AddSysCustomKey(key); 117 return await Do(db => db.StringSetAsync(key, value, expiry)); 118 } 119 120 /// <summary> 121 /// 保存多個key value 122 /// </summary> 123 /// <param name="keyValues">鍵值對</param> 124 /// <returns></returns> 125 public async Task<bool> StringSetAsync(List<KeyValuePair<RedisKey, RedisValue>> keyValues) 126 { 127 List<KeyValuePair<RedisKey, RedisValue>> newkeyValues = 128 keyValues.Select(p => new KeyValuePair<RedisKey, RedisValue>(AddSysCustomKey(p.Key), p.Value)).ToList(); 129 return await Do(db => db.StringSetAsync(newkeyValues.ToArray())); 130 } 131 132 /// <summary> 133 /// 保存一個對象 134 /// </summary> 135 /// <typeparam name="T"></typeparam> 136 /// <param name="key"></param> 137 /// <param name="obj"></param> 138 /// <param name="expiry"></param> 139 /// <returns></returns> 140 public async Task<bool> StringSetAsync<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?)) 141 { 142 key = AddSysCustomKey(key); 143 string json = ConvertJson(obj); 144 return await Do(db => db.StringSetAsync(key, json, expiry)); 145 } 146 147 /// <summary> 148 /// 獲取單個key的值 149 /// </summary> 150 /// <param name="key">Redis Key</param> 151 /// <returns></returns> 152 public async Task<string> StringGetAsync(string key) 153 { 154 key = AddSysCustomKey(key); 155 return await Do(db => db.StringGetAsync(key)); 156 } 157 158 /// <summary> 159 /// 獲取多個Key 160 /// </summary> 161 /// <param name="listKey">Redis Key集合</param> 162 /// <returns></returns> 163 public async Task<RedisValue[]> StringGetAsync(List<string> listKey) 164 { 165 List<string> newKeys = listKey.Select(AddSysCustomKey).ToList(); 166 return await Do(db => db.StringGetAsync(ConvertRedisKeys(newKeys))); 167 } 168 169 /// <summary> 170 /// 獲取一個key的對象 171 /// </summary> 172 /// <typeparam name="T"></typeparam> 173 /// <param name="key"></param> 174 /// <returns></returns> 175 public async Task<T> StringGetAsync<T>(string key) 176 { 177 key = AddSysCustomKey(key); 178 string result = await Do(db => db.StringGetAsync(key)); 179 return ConvertObj<T>(result); 180 } 181 182 /// <summary> 183 /// 為數字增長val 184 /// </summary> 185 /// <param name="key"></param> 186 /// <param name="val">可以為負</param> 187 /// <returns>增長後的值</returns> 188 public async Task<double> StringIncrementAsync(string key, double val = 1) 189 { 190 key = AddSysCustomKey(key); 191 return await Do(db => db.StringIncrementAsync(key, val)); 192 } 193 194 /// <summary> 195 /// 為數字減少val 196 /// </summary> 197 /// <param name="key"></param> 198 /// <param name="val">可以為負</param> 199 /// <returns>減少後的值</returns> 200 public async Task<double> StringDecrementAsync(string key, double val = 1) 201 { 202 key = AddSysCustomKey(key); 203 return await Do(db => db.StringDecrementAsync(key, val)); 204 } 205 206 #endregion 非同步方法 207 208 #endregion String