最近在自己的項目有使用到redis,一直使用別人封裝的東西,今天想自己動手封裝一下,順便深入了解redis。
安裝
我使用的StackExchange.Redis
GitHub:https://github.com/StackExchange/StackExchange.Redis/
安裝最新版本就行
封裝
官網有這樣一句話
The central object in StackExchange.Redis is the ConnectionMultiplexer
class in the StackExchange.Redis
namespace; this is the object that hides away the details of multiple servers. Because the ConnectionMultiplexer
does a lot, it is designed to be shared and reused between callers. You should not create a ConnectionMultiplexer
per operation. It is fully thread-safe and ready for this usage. In all the subsequent examples it will be assumed that you have a ConnectionMultiplexer
instance stored away for re-use.
通過百度翻譯
中心對象StackExchange.Redis中的ConnectionMultiplexer類StackExchange.Redis名稱空間;這是隱藏多個服務器詳細信息的對象。因為ConnectionMultiplexer做了很多事情,所以它被設計成在調用者之間共享和重用。不應為每個操作創建ConnectionMultiplexer。它是完全線程安全的,可以使用它。在后面的所有示例中,我們將假定您有一個ConnectionMultiplexer實例存儲起來以供重用。
那我可以封裝一個Redis連接類,這個類型可以單例模式封裝成一個單例。
public class RedisConnectionHelper { /// <summary> /// 連接redis server 類 /// </summary> public static ConnectionMultiplexer _connection; public readonly static object _locker = new object(); /// <summary> /// 緩存連接實例 /// </summary> public static readonly Dictionary<string, ConnectionMultiplexer> _connectionCache = new Dictionary<string, ConnectionMultiplexer>(); /// <summary> /// 連接實例 (單例模式) /// </summary> public static ConnectionMultiplexer Instance { get { if (_connection == null) { lock (_locker) { if (_connection == null || !_connection.IsConnected) { _connection = CreateConnection(); } } } return _connection; } } /// <summary> /// https://stackexchange.github.io/StackExchange.Redis/Basics /// </summary> /// <param name="connectionString"></param> public static ConnectionMultiplexer CreateConnection(string connectionString = null) { //連接字符串 string connectStr = connectionString ?? "127.0.0.1:6379"; ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(connectStr); //redis 事件注冊 redis.ConnectionFailed += Redis_ConnectionFailed; return redis; } public static ConnectionMultiplexer GetConnectionMultiplexer(string connectionString) { if (!_connectionCache.ContainsKey(connectionString)) { _connectionCache[connectionString] = CreateConnection(connectionString); } return _connectionCache[connectionString]; } /// <summary> /// redis 服務連接失敗事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void Redis_ConnectionFailed(object sender, ConnectionFailedEventArgs e) { Console.WriteLine(e.Exception); } }
實例化ConnectionMultiplexer對象之后,可以直接GetDatabase獲取到Redis數據庫對象(IDatabase類型)。
這其中有很多操作數據的方法,我每次在實例化Db之后再去調用方法,有點麻煩,再封裝一個方法類,需要注意的是這里封裝方法需要安裝Newtonsoft.Json
這里為什么需要用到這跟呢,因為很多情況下,如果我需要緩存一個對象的時候,例如一個Student,那么這種情況下,Redis是沒法直接存的,需要先Json化為string類型
然后再保存。
這里重點要注意的是操作list的方法,redis中的list是一個雙向鏈表,list集合可以左右、中間插入元素。所以在這里在封裝的時候還是有一些坑去爬的。
public static class RedisHelper { public static readonly ConnectionMultiplexer _connectin; /// <summary> /// 數據庫id /// </summary> public static int DbNum { get; set; } /// <summary> /// 連接方式 /// </summary> public static string ConnectionString { get; set; } static RedisHelper() { DbNum = -1; ConnectionString = null; _connectin = string.IsNullOrEmpty(ConnectionString) ? RedisConnectionHelper.Instance : RedisConnectionHelper.GetConnectionMultiplexer(ConnectionString); } /// <summary> /// 獲取db /// </summary> public static IDatabase Db { get { return _connectin.GetDatabase(DbNum); } } /// <summary> /// 執行委托 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="func"></param> /// <returns></returns> public static T Do<T>(Func<IDatabase, T> func) { return func(Db); } #region string /// <summary> /// 保存string /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiry"></param> public static bool StringSet(string key, string value, TimeSpan? expiry = null) { return Do(d => d.StringSet(key, value,expiry)); } /// <summary> /// 異步保存 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiry"></param> /// <returns></returns> public static async Task<bool> StringSetAsync(string key, string value, TimeSpan? expiry = null) { return await Do(d => d.StringSetAsync(key, value, expiry)); } /// <summary> /// 獲取值 /// </summary> /// <param name="key"></param> /// <returns></returns> public static string StringGet(string key) { return Do(d => d.StringGet(key)); } /// <summary> /// 獲取值 /// </summary> /// <param name="key"></param> /// <returns></returns> public static async Task<string> StringGetAsync(string key) { return await Do(d => d.StringGetAsync(key)); } /// <summary> /// 刪除鍵 /// </summary> /// <param name="key"></param> public static bool KeyDelete(string key) { return Do(d => d.KeyDelete(key)); } /// <summary> /// 刪除鍵 /// </summary> /// <param name="key"></param> public static async Task<bool> KeyDeleteAsync(string key) { return await Do(d => d.KeyDeleteAsync(key)); } /// <summary> /// set /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiry"></param> /// <returns></returns> public static bool StringSet<T>(string key, T value, TimeSpan? expiry = null) { string json = Serialize(value); return Do(d => d.StringSet(key, json, expiry)); } /// <summary> /// set /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiry"></param> /// <returns></returns> public static async Task<bool> StringSetAsync<T>(string key, T value, TimeSpan? expiry = null) { string json = Serialize(value); return await Do(d => d.StringSetAsync(key, json, expiry)); } /// <summary> /// get /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiry"></param> /// <returns></returns> public static T StringGet<T>(string key) { string json = Do(d => d.StringGet(key)).ToString(); return Deserialize<T>(json); } /// <summary> /// get /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiry"></param> /// <returns></returns> public static async Task<T> StringGetAsync<T>(string key) { string json = await Do(d => d.StringGetAsync(key)); return Deserialize<T>(json); } #endregion /// <summary> /// 序列化 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="value"></param> /// <returns></returns> private static string Serialize<T>(T value) { var result = value is string ? value.ToString() : JsonConvert.SerializeObject(value, new JsonSerializerSettings { DateFormatString = "yyyy-MM-dd hh:mm:ss" }); return result; } /// <summary> /// 解析 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="json"></param> /// <returns></returns> private static T Deserialize<T>(string json) { if (string.IsNullOrEmpty(json)) return default; return JsonConvert.DeserializeObject<T>(json); } /// <summary> /// 解析redis緩存的集合 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="values"></param> /// <returns></returns> private static List<T> DeserializeList<T>(RedisValue[] values) { return values.Select(s => Deserialize<T>(s)).ToList(); } #region List /// <summary> /// 保存list 集合 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> public static void SetList<T>(string key, List<T> value) { if (value.Count > 0) { var values = value.Select(s => (RedisValue)Serialize(s)).ToArray(); Do(d => d.ListRightPush(key, values)); } } /// <summary> /// 保存list 集合 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> public static async Task SetListAsync<T>(string key, List<T> value) { if (value.Count > 0) { var values = value.Select(s => (RedisValue)Serialize(s)).ToArray(); await Do(d => d.ListRightPushAsync(key, values)); } } /// <summary> /// 獲取list 集合 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public static List<T> GetList<T>(string key) { var values = Do(d => d.ListRange(key)); if (values.IsNullOrEmpty()) { return values.Select(s => Deserialize<T>(s)).ToList(); } return default; } /// <summary> /// 獲取list 集合 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public static async Task<List<T>> GetListAsync<T>(string key) { var values = await Do(d => d.ListRangeAsync(key)); if (values.IsNullOrEmpty()) { return values.Select(s => Deserialize<T>(s)).ToList(); } return default; } /// <summary> /// 刪除並返回集合的第一個元素 /// ListLeftPop /// </summary> /// <param name="key"></param> public static T RemoveFirst<T>(string key) { var json = Do(d => d.ListLeftPop(key)); return Deserialize<T>(json); } /// <summary> /// 刪除並返回集合的第一個元素 /// ListLeftPop /// </summary> /// <param name="key"></param> public static async Task<T> RemoveFirstAsync<T>(string key) { var json = await Do(d => d.ListLeftPopAsync(key)); return Deserialize<T>(json); } /// <summary> /// 刪除並返回集合的最后一個元素 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public static T RemoveLast<T>(string key) { return Deserialize<T>(Do(d => d.ListRightPop(key))); } /// <summary> /// 刪除並返回集合的最后一個元素 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public static async Task<T> RemoveLastAsync<T>(string key) { return Deserialize<T>(await Do(d => d.ListRightPopAsync(key))); } /// <summary> /// 根據索引獲取集合 (默認所有) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="start"></param> /// <param name="stop"></param> /// <returns></returns> public static List<T> ListRange<T>(string key, long start = 0, long stop = -1) { var json = Do(d => d.ListRange(key, start, stop)); return DeserializeList<T>(json); } /// <summary> /// 根據索引獲取集合 (默認所有) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="start"></param> /// <param name="stop"></param> /// <returns></returns> public static async Task<List<T>> ListRangeAsync<T>(string key, long start = 0, long stop = -1) { var json = await Do(d => d.ListRangeAsync(key, start, stop)); return DeserializeList<T>(json); } /// <summary> /// 根據元素刪除 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <returns>返回刪除元素數量</returns> public static int RemoveAt<T>(string key,T value) { return (int)Do(d => d.ListRemove(key, Serialize(value))); } /// <summary> /// 根據元素刪除 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <returns>返回刪除元素數量</returns> public static async Task<int> RemoveAtAsync<T>(string key, T value) { return (int)await Do(d => d.ListRemoveAsync(key, Serialize(value))); } /// <summary> /// 向集合開頭添加一個元素 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> public static void AddFirst<T>(string key, T value) { Do(d => d.ListLeftPush(key, Serialize(value))); } /// <summary> /// 向集合開頭添加一個元素 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> public static async Task AddFirstAsync<T>(string key, T value) { await Do(d => d.ListLeftPushAsync(key, Serialize(value))); } /// <summary> /// 向集合末尾添加一個元素 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> public static void AddLast<T>(string key, T value) { Do(d => d.ListRightPush(key, Serialize(value))); } /// <summary> /// 向集合末尾添加一個元素 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> public static async Task AddLastAsync<T>(string key, T value) { await Do(d => d.ListRightPushAsync(key, Serialize(value))); } /// <summary> /// 判斷對象是否存在 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static bool Contains<T>(string key, T value) { //數量 var length = Count(key); //格式話對象 var json = Serialize(value); for (int i = 0; i < length; i++) { if (Do(d => d.ListGetByIndex(key, i).ToString().Equals(json))) { return true; } } return false; } /// <summary> /// 判斷對象是否存在 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static async Task<bool> ContainsAsync<T>(string key, T value) { //數量 var length = await CountAsync(key); //格式話對象 var json = Serialize(value); for (int i = 0; i < length; i++) { var item = await Do(d => d.ListGetByIndexAsync(key, i)); if (item.ToString().Equals(json)) { return true; } } return false; } /// <summary> /// 根據索引獲取對象 /// ListGetByIndex /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="index"></param> /// <returns></returns> public static T Get<T>(string key, int index) { string json = Do(d => d.ListGetByIndex(key, index)); return Deserialize<T>(json); } /// <summary> /// 根據索引獲取對象 /// ListGetByIndex /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="index"></param> /// <returns></returns> public static async Task<T> GetAsync<T>(string key, int index) { string json = await Do(d => d.ListGetByIndexAsync(key, index)); return Deserialize<T>(json); } /// <summary> /// 根據對象獲取索引 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static int IndexOf<T>(string key, T value) { //數量 var length = Count(key); //格式話對象 var json = Serialize(value); for (int i = 0; i < length; i++) { if ( Do(d => d.ListGetByIndex(key, i).ToString().Equals(json))) { return i; } } return -1; } /// <summary> /// 根據對象獲取索引 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static async Task<int> IndexOfAsync<T>(string key, T value) { //數量 var length = await CountAsync(key); //格式話對象 var json = Serialize(value); for (int i = 0; i < length; i++) { var item = await Do(d => d.ListGetByIndexAsync(key, i)); if ( item.ToString().Equals(json)) { return i; } } return -1; } /// <summary> /// 獲取list的數量 /// </summary> /// <param name="key"></param> /// <returns></returns> public static int Count(string key) { return (int)Do(d => d.ListLength(key)); } /// <summary> /// 獲取list的數量 /// </summary> /// <param name="key"></param> /// <returns></returns> public static async Task<int> CountAsync(string key) { return (int)await Do(d => d.ListLengthAsync(key)); } #endregion }
總結
很多東西需要自己動手去試試才能獲得更多知識。
封裝GitHub:https://github.com/QQ2287991080/Zero.Core/tree/master/src/Zero.Core.Common/Redis
測試GitHub:https://github.com/QQ2287991080/Zero.Core/blob/master/src/Zero.Core.xUnitTest/Redis/RedisTest.cs