最近在自己的项目有使用到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