Redis在当下的互联网项目当中的普及率我想都不用多说了,本文要介绍的這个项目是基于我对Redis理解程度的基础上写的一个公共类库项目,希望对各位童鞋有所帮助,也欢迎各位对我都内容提出更好的意见。
由于本文使用了自定义配置相关的只是,如有不了解的童鞋,可以先去了解一下這方面的知识然后再来看相应的代码,這样可能想过会更好,下面正式进入正题(初次写這个东西,语言组织不合理请大家谅解)。
项目概览:
该项目主要分成两部分,配置和业务扩展两部分组成,具体项目结构如下:
自定义配置:建立配置项的目的主要是方便我们后面在web.config或者app.config中定义属于redis的单独配置区域,从而使得我们的程序配置变得简单、易读易配置,后面的介绍将不再重复说明
RedisConfiguration.cs: 作为自定义配置文件的主节点,该节点包含的属性: serviceStackConnectionStrings和stackExchangeConnectionStrings
1 using System.Configuration; 2 3 namespace HB.Common.Caches.Configs 4 { 5 /// <summary> 6 /// Redis配置节点 7 /// </summary> 8 public class RedisConfiguration : ConfigurationSection 9 { 10 //<configSections> 11 // <section name = "redisConfiguration" type="HB.Common.Caches.Configs.RedisConfiguration, HB.Common.Caches.Configs" /> 12 //</configSections> 13 //<redisConfiguration configSource = "Config\redisConfiguration.config" /> 14 15 //<redisConfiguration> 16 // <!--连接字符串--> 17 // <serviceStackConnectionStrings writehost ="192.168.3.11:6379,192.168.3.11:6380" readhost="192.168.3.11:6379,192.168.3.11:6380" password="ChinaNet910111" maxwritepoolsize="200" maxreadpoolsize="200" /> 18 //</redisConfiguration> 19 20 /// <summary> 21 /// 节点名称 22 /// </summary> 23 private const string sectionName = "redisConfiguration"; 24 25 /// <summary> 26 /// 获取配置信息 27 /// </summary> 28 /// <returns></returns> 29 public static RedisConfiguration ConfigSection() 30 { 31 return (RedisConfiguration)ConfigurationManager.GetSection(sectionName); 32 } 33 34 /// <summary> 35 /// StackExchange配置节点 36 /// </summary> 37 [ConfigurationProperty("stackExchangeConnectionStrings", IsRequired = false)] 38 public StackExchangeConfigurationSection StackExchangeConnectionStrings 39 { 40 get 41 { 42 return (StackExchangeConfigurationSection)base["stackExchangeConnectionStrings"]; 43 } 44 set 45 { 46 base["stackExchangeConnectionStrings"] = value; 47 } 48 } 49 50 /// <summary> 51 /// ServiceStack配置节点 52 /// </summary> 53 [ConfigurationProperty("serviceStackConnectionStrings", IsRequired = false)] 54 public ServiceStackConfigurationSection ServiceStackConnectionStrings 55 { 56 get 57 { 58 return (ServiceStackConfigurationSection)base["serviceStackConnectionStrings"]; 59 } 60 set 61 { 62 base["serviceStackConnectionStrings"] = value; 63 } 64 } 65 } 66 }
RedisConfiguration.cs
ServiceStackConfigurationSection.cs:ServiceStack插件链接字符串配置项
1 using System.Configuration; 2 3 namespace HB.Common.Caches.Configs 4 { 5 /// <summary> 6 /// ServiceStack配置节点 7 /// </summary> 8 public class ServiceStackConfigurationSection : ConfigurationElement 9 { 10 //<!--连接字符串--> 11 //<serviceStackConnectionStrings writehost ="192.168.3.11:6379,192.168.3.11:6380" readhost="192.168.3.11:6379,192.168.3.11:6380" password="ChinaNet910111" maxwritepoolsize="200" maxreadpoolsize="200" autoStart="false" /> 12 13 /// <summary> 14 /// 可写的Redis链接地址 15 /// </summary> 16 [ConfigurationProperty("writehost", IsRequired = false)] 17 public string WriteHost 18 { 19 get 20 { 21 return (string)base["writehost"]; 22 } 23 set 24 { 25 base["writehost"] = value; 26 } 27 } 28 29 /// <summary> 30 /// 可读的Redis链接地址 31 /// </summary> 32 [ConfigurationProperty("readhost", IsRequired = false)] 33 public string ReadHost 34 { 35 get 36 { 37 return (string)base["readhost"]; 38 } 39 set 40 { 41 base["readhost"] = value; 42 } 43 } 44 45 /// <summary> 46 /// 登陆密码 47 /// </summary> 48 [ConfigurationProperty("password", IsRequired = false)] 49 public string Password 50 { 51 get 52 { 53 return (string)base["password"]; 54 } 55 set 56 { 57 base["password"] = value; 58 } 59 } 60 61 /// <summary> 62 /// 最大写链接数 63 /// </summary> 64 [ConfigurationProperty("maxwritepoolsize", IsRequired = false, DefaultValue = 200)] 65 public int MaxWritePoolSize 66 { 67 get 68 { 69 return (int)base["maxwritepoolsize"]; 70 } 71 set 72 { 73 base["maxwritepoolsize"] = value; 74 } 75 } 76 77 /// <summary> 78 /// 最大读链接数 79 /// </summary> 80 [ConfigurationProperty("maxreadpoolsize", IsRequired = false, DefaultValue = 5)] 81 public int MaxReadPoolSize 82 { 83 get 84 { 85 return (int)base["maxreadpoolsize"]; 86 } 87 set 88 { 89 base["maxreadpoolsize"] = value; 90 } 91 } 92 93 /// <summary> 94 /// 95 /// </summary> 96 [ConfigurationProperty("autoStart", IsRequired = false, DefaultValue = true)] 97 public bool AutoStart 98 { 99 get 100 { 101 return (bool)base["autoStart"]; 102 } 103 set 104 { 105 base["autoStart"] = value; 106 } 107 } 108 } 109 }
ServiceStackConfigurationSection.cs
StackExchangeConfigurationSection.cs:StackExchange.Redis的链接字符串属性节点
1 using System.Configuration; 2 3 namespace HB.Common.Caches.Configs 4 { 5 /// <summary> 6 /// StackExchange节点配置 7 /// </summary> 8 public class StackExchangeConfigurationSection : ConfigurationElement 9 { 10 // <!--连接字符串--> 11 // <connectionStrings host = "192.168.3.11:6379,192.168.3.11:6380" password="ChinaNet910111" /> 12 13 /// <summary> 14 /// 监听主机 15 /// </summary> 16 [ConfigurationProperty("host", IsRequired = true)] 17 public string Host 18 { 19 get 20 { 21 return (string)base["host"]; 22 } 23 set 24 { 25 base["host"] = value; 26 } 27 } 28 29 /// <summary> 30 /// 密码 31 /// </summary> 32 [ConfigurationProperty("password", IsRequired = false)] 33 public string Password 34 { 35 get 36 { 37 return (string)base["password"]; 38 } 39 set 40 { 41 base["password"] = value; 42 } 43 } 44 } 45 }
StackExchangeConfigurationSection.cs
业务扩展类:
ServiceStackManager.cs:该管理类ServiceStack的扩展,可以对Redis进行增、删、改、查等一系列操作,具体实现如下
1 using HB.Common.Caches.Configs; 2 using ServiceStack.Redis; 3 4 namespace HB.Common.Caches.Redis 5 { 6 /// <summary> 7 /// Redis客户端管理类 8 /// </summary> 9 public class ServiceStackManager 10 { 11 /// <summary> 12 /// 线程同步变量 13 /// </summary> 14 private static object synObj = new object(); 15 16 /// <summary> 17 /// redis链接池管理对象 18 /// </summary> 19 private static PooledRedisClientManager _instance = null; 20 21 /// <summary> 22 /// 链接字符串 23 /// </summary> 24 private static ServiceStackConfigurationSection ConnectionStrings 25 { 26 get 27 { 28 var configSection = RedisConfiguration.ConfigSection(); 29 return configSection.ServiceStackConnectionStrings; 30 } 31 } 32 33 /// <summary> 34 /// 私有构造函数,静止外部通过new关键字来创建该对象实例 35 /// </summary> 36 private ServiceStackManager() 37 { 38 39 } 40 41 /// <summary> 42 /// 获取redis链接池管理对象实例 43 /// 实例发生变化的集中情况: 44 /// 1.实例为空 45 /// 2.配置文件发生变化 46 /// </summary> 47 private static PooledRedisClientManager GetInstance() 48 { 49 if (_instance == null) 50 { 51 lock (synObj) 52 { 53 if (_instance == null) 54 { 55 var managerConfig = new RedisClientManagerConfig() 56 { 57 AutoStart = ConnectionStrings.AutoStart, 58 MaxReadPoolSize = ConnectionStrings.MaxReadPoolSize, 59 MaxWritePoolSize = ConnectionStrings.MaxWritePoolSize 60 }; 61 62 string[] readServerList = GetHostList(ConnectionStrings.ReadHost); 63 string[] writeServerList = GetHostList(ConnectionStrings.WriteHost); 64 _instance = new PooledRedisClientManager(readServerList, writeServerList, managerConfig); 65 } 66 } 67 } 68 return _instance; 69 } 70 71 /// <summary> 72 /// 客户端缓存操作对象实例 73 /// </summary> 74 public static IRedisClient GetClient(int dbIndex = 0) 75 { 76 var clientManager = GetInstance(); 77 var client = clientManager.GetClient(); 78 if (!string.IsNullOrEmpty(ConnectionStrings.Password)) 79 client.Password = ConnectionStrings.Password; 80 81 client.Db = dbIndex; 82 return client; 83 } 84 85 /// <summary> 86 /// 87 /// </summary> 88 /// <param name="hostStrings"></param> 89 /// <returns></returns> 90 private static string[] GetHostList(string hostStrings) 91 { 92 if (string.IsNullOrWhiteSpace(hostStrings)) 93 return new string[] { }; 94 95 return hostStrings.Split(‘,‘); 96 } 97 } 98 }
ServiceStackManager.cs
StackExchangeManager.cs:该管理类是对StackExchange.Redis增删改查等操作的扩展
1 using HB.Common.Caches.Configs; 2 using log4net; 3 using Newtonsoft.Json; 4 using StackExchange.Redis; 5 6 namespace HB.Common.Caches.Redis 7 { 8 /// <summary> 9 /// StackExchangeManager 10 /// 11 /// 在StackExchange.Redis中最重要的对象是ConnectionMultiplexer类, 它存在于StackExchange.Redis命名空间中。 12 /// 这个类隐藏了Redis服务的操作细节,ConnectionMultiplexer类做了很多东西, 在所有调用之间它被设计为共享和重用的。 13 /// 不应该为每一个操作都创建一个ConnectionMultiplexer 。 ConnectionMultiplexer是线程安全的 , 推荐使用下面的方法。 14 /// 在所有后续示例中 , 都假定你已经实例化好了一个ConnectionMultiplexer类,它将会一直被重用 , 15 /// 现在我们来创建一个ConnectionMultiplexer实例。它是通过ConnectionMultiplexer.Connect 或者 ConnectionMultiplexer.ConnectAsync, 16 /// 传递一个连接字符串或者一个ConfigurationOptions 对象来创建的。 17 /// 连接字符串可以是以逗号分割的多个服务的节点. 18 /// 19 /// 注意 : 20 /// ConnectionMultiplexer 实现了IDisposable接口当我们不再需要是可以将其释放的 , 这里我故意不使用 using 来释放他。 21 /// 简单来讲创建一个ConnectionMultiplexer是十分昂贵的 , 一个好的主意是我们一直重用一个ConnectionMultiplexer对象。 22 /// 一个复杂的的场景中可能包含有主从复制 , 对于这种情况,只需要指定所有地址在连接字符串中(它将会自动识别出主服务器) 23 /// ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:6379,server2:6379"); 24 /// 假设这里找到了两台主服务器,将会对两台服务进行裁决选出一台作为主服务器来解决这个问题 , 这种情况是非常罕见的 ,我们也应该避免这种情况的发生。 25 /// 26 /// 27 /// 这里有个和 ServiceStack.Redis 大的区别是没有默认的连接池管理了。没有连接池自然有其利弊,最大的好处在于等待获取连接的等待时间没有了, 28 /// 也不会因为连接池里面的连接由于没有正确释放等原因导致无限等待而处于死锁状态。缺点在于一些低质量的代码可能导致服务器资源耗尽。不过提供连接池等阻塞和等待的手段是和作者的设计理念相违背的。StackExchange.Redis这里使用管道和多路复用的技术来实现减少连接 29 /// 30 /// 参考:http://www.cnblogs.com/Leo_wl/p/4968537.html 31 /// </summary> 32 public class StackExchangeManager 33 { 34 /// <summary> 35 /// 线程同步变量 36 /// </summary> 37 private static object syncObj = new object(); 38 39 /// <summary> 40 /// redis链接池管理对象 41 /// </summary> 42 private static ConnectionMultiplexer _instance = null; 43 44 /// <summary> 45 /// 日志记录器 46 /// </summary> 47 private static readonly ILog _log = LogManager.GetLogger(typeof(StackExchangeManager)); 48 49 /// <summary> 50 /// 私有构造函数,限制不允许通过new 来实例化该对象 51 /// </summary> 52 private StackExchangeManager() 53 { 54 55 } 56 57 /// <summary> 58 /// 使用一个静态属性来返回已连接的实例 59 /// 实例发生变化的几种情况: 60 /// 1.实例为空 61 /// 2.连接关闭 62 /// 3.文件发生变化时 63 /// </summary> 64 private static ConnectionMultiplexer GetInstance() 65 { 66 if (_instance == null || !_instance.IsConnected) 67 { 68 lock (syncObj) 69 { 70 if (_instance == null || !_instance.IsConnected) 71 { 72 var configSection = RedisConfiguration.ConfigSection(); 73 var stackExchangeConnectionStrings = configSection.StackExchangeConnectionStrings; 74 _instance = ConnectionMultiplexer.Connect(stackExchangeConnectionStrings.Host); 75 } 76 } 77 } 78 _instance.ErrorMessage += MuxerErrorMessage; 79 _instance.HashSlotMoved += MuxerHashSlotMoved; 80 _instance.InternalError += MuxerInternalError; 81 _instance.ConnectionFailed += MuxerConnectionFailed; 82 _instance.ConnectionRestored += MuxerConnectionRestored; 83 _instance.ConfigurationChanged += MuxerConfigurationChanged; 84 return _instance; 85 } 86 87 /// <summary> 88 /// 客户端缓存操作对象实例 89 /// <paramref name="dbIndex"/> 90 /// </summary> 91 public static IDatabase GetDatabase(int dbIndex = -1) 92 { 93 var instance = GetInstance(); 94 return instance.GetDatabase(dbIndex); 95 } 96 97 /// <summary> 98 /// 配置更改时 99 /// </summary> 100 /// <param name="sender"></param> 101 /// <param name="e"></param> 102 private static void MuxerConfigurationChanged(object sender, EndPointEventArgs e) 103 { 104 _log.Warn($"Muxer Configuration Changed=>EndPoint:{JsonConvert.SerializeObject(e.EndPoint)}"); 105 } 106 107 /// <summary> 108 /// 发生错误时 109 /// </summary> 110 /// <param name="sender"></param> 111 /// <param name="e"></param> 112 private static void MuxerErrorMessage(object sender, RedisErrorEventArgs e) 113 { 114 _log.Error($"Muxer ErrorMessage=>RedisErrorEventArgs:{JsonConvert.SerializeObject(e)}"); 115 } 116 117 /// <summary> 118 /// 重新建立连接之前的错误 119 /// </summary> 120 /// <param name="sender"></param> 121 /// <param name="e"></param> 122 private static void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e) 123 { 124 _log.Error($"Muxer Connection Restored=>ConnectionFailedEventArgs:{JsonConvert.SerializeObject(e)}"); 125 } 126 127 /// <summary> 128 /// 连接失败 , 如果重新连接成功你将不会收到这个通知 129 /// </summary> 130 /// <param name="sender"></param> 131 /// <param name="e"></param> 132 private static void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e) 133 { 134 _log.Error($"Muxer Connection Failed=>ConnectionFailedEventArgs:{JsonConvert.SerializeObject(e)}"); 135 } 136 137 /// <summary> 138 /// 更改集群 139 /// </summary> 140 /// <param name="sender"></param> 141 /// <param name="e"></param> 142 private static void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e) 143 { 144 _log.Error($"Muxer HashSlot Moved=>HashSlotMovedEventArgs:{JsonConvert.SerializeObject(e)}"); 145 } 146 147 /// <summary> 148 /// redis类库错误 149 /// </summary> 150 /// <param name="sender"></param> 151 /// <param name="e"></param> 152 private static void MuxerInternalError(object sender, InternalErrorEventArgs e) 153 { 154 _log.Error($"Muxer Internal Error=>InternalErrorEventArgs:{JsonConvert.SerializeObject(e)}"); 155 } 156 } 157 }
StackExchangeManager.cs
RedisSessionManager.cs:主要是StackExchange.Redis插件实现的用户登陆会话管理类
1 using Newtonsoft.Json; 2 using StackExchange.Redis; 3 using System; 4 using System.Web; 5 6 namespace HB.Common.Caches.Redis 7 { 8 /// <summary> 9 /// Redis Session 管理类 10 /// </summary> 11 public class RedisSessionManager 12 { 13 /// <summary> 14 /// 获取并设置在会话状态提供程序终止会话之前各请求之间所允许的时间(以分钟为单位) 15 /// </summary> 16 public int TimeOut { get; set; } 17 18 /// <summary> 19 /// 获取一个值,该值指示会话是否为只读 20 /// </summary> 21 public bool IsReadOnly { get; set; } 22 23 /// <summary> 24 /// HttpContext 25 /// </summary> 26 private HttpContextBase HttpContext { get; set; } 27 28 /// <summary> 29 /// SessionId标识符 30 /// </summary> 31 public static string SessionName = "HB.Redis.SessionId"; 32 33 /// <summary> 34 /// 缓存 35 /// </summary> 36 private static IDatabase _cache = StackExchangeManager.GetDatabase(); 37 38 /// <summary> 39 /// 获取会话状态集合中的项数 40 /// </summary> 41 public long Count 42 { 43 get 44 { 45 return _cache.HashLength(SessionId); 46 } 47 } 48 49 /// <summary> 50 /// 静止无参的构造函数被实例化 51 /// </summary> 52 private RedisSessionManager() { } 53 54 /// <summary> 55 /// 构造函数 56 /// </summary> 57 /// <param name="httpRequst">客户端的请求信息</param> 58 /// <param name="isReadOnly"> 59 /// 获取一个值,该值指示会话是否为只读 60 /// 说明:true 表示只读;false 表示读写 61 /// 默认:true 62 /// </param> 63 /// <param name="timeout"> 64 /// 会话有效期 65 /// 单位:分钟 66 /// 默认:20分钟 67 /// </param> 68 public RedisSessionManager(HttpContextBase httpContext, bool isReadOnly = true, int timeout = 20) 69 { 70 TimeOut = timeout; 71 IsReadOnly = isReadOnly; 72 HttpContext = httpContext; 73 if (_cache.KeyExists(SessionId)) 74 { 75 //设置缓存有效期(第二次以上通过這里更新缓存有效期) 76 _cache.KeyExpire(SessionId, expiry: DateTime.Now.AddMinutes(TimeOut)); 77 } 78 } 79 80 /// <summary> 81 /// 获取并设置在会话状态提供程序终止会话之前各请求之间所允许的时间(以分钟为单位) 82 /// </summary> 83 /// <returns></returns> 84 public string SessionId 85 { 86 get 87 { 88 var cookie = HttpContext.Request.Cookies.Get(SessionName); 89 if (cookie == null || string.IsNullOrEmpty(cookie.Value)) 90 { 91 string newSessionID = Guid.NewGuid().ToString(); 92 var newCookie = new HttpCookie(SessionName, newSessionID); 93 newCookie.HttpOnly = IsReadOnly; 94 newCookie.Expires = DateTime.Now.AddMinutes(TimeOut); 95 HttpContext.Response.Cookies.Add(newCookie); 96 return newSessionID; 97 } 98 else 99 { 100 return cookie.Value; 101 } 102 } 103 } 104 105 /// <summary> 106 /// 按名称获取或者设置会话值 107 /// </summary> 108 /// <param name="name"></param> 109 /// <returns></returns> 110 public object this[string name] 111 { 112 get 113 { 114 return _cache.HashGet(SessionId, name); 115 } 116 set 117 { 118 var isKeyExists = _cache.KeyExists(SessionId);//缓存是否存在 119 var hashValue = JsonConvert.SerializeObject(value); 120 var isSuccess = _cache.HashSet(SessionId, name, hashValue); 121 if (isSuccess && !isKeyExists) 122 { 123 //设置缓存有效期(第一次插入缓存的时候通过這里设置缓存的有效期) 124 _cache.KeyExpire(SessionId, expiry: DateTime.Now.AddMinutes(TimeOut)); 125 } 126 } 127 } 128 129 /// <summary> 130 /// 判断会话中是否存在指定key 131 /// </summary> 132 /// <param name="name">键值</param> 133 /// <returns></returns> 134 public bool Exists(string name) 135 { 136 return _cache.HashExists(SessionId, name); 137 } 138 139 /// <summary> 140 /// 从会话集合中移除所有的键值 141 /// </summary> 142 /// <returns></returns> 143 public bool Clear() 144 { 145 return _cache.KeyDelete(SessionId); 146 } 147 148 /// <summary> 149 /// 删除会话状态集合中的指定项 150 /// </summary> 151 /// <param name="name"></param> 152 /// <returns></returns> 153 public bool Remover(string name) 154 { 155 return _cache.HashDelete(SessionId, name); 156 } 157 } 158 }
RedisSessionManager.cs
有需要源码的同学,可以给我留言或者发到我得邮箱。