C#实现的Redis缓存公共类库项目

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#实现的Redis缓存公共类库项目相关的知识,希望对你有一定的参考价值。

    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

有需要源码的同学,可以给我留言或者发到我得邮箱。

以上是关于C#实现的Redis缓存公共类库项目的主要内容,如果未能解决你的问题,请参考以下文章

Redis C#缓存的使用

项目实战 redis 缓存

C# - Lee 公共类库

springboot+mybatis+redis实现分布式缓存

Java内存缓存工具实现 - Guava LoadingCache

从 Apollo 缓存中读取特定类型的所有片段