Redis在当下的互联网项目当中的普及率我想都不用多说了,本文要介绍的這个项目是基于我对Redis理解程度的基础上写的一个公共类库项目,希望对各位童鞋有所帮助,也欢迎各位对我都内容提出更好的意见。
由于本文使用了自定义配置相关的只是,如有不了解的童鞋,可以先去了解一下這方面的知识然后再来看相应的代码,這样可能想过会更好,下面正式进入正题(初次写這个东西,语言组织不合理请大家谅解)。
项目概览:
该项目主要分成两部分,配置和业务扩展两部分组成,具体项目结构如下:
自定义配置:建立配置项的目的主要是方便我们后面在web.config或者app.config中定义属于redis的单独配置区域,从而使得我们的程序配置变得简单、易读易配置,后面的介绍将不再重复说明
RedisConfiguration.cs: 作为自定义配置文件的主节点,该节点包含的属性: serviceStackConnectionStrings和stackExchangeConnectionStrings
using System.Configuration; namespace HB.Common.Caches.Configs
{
/// <summary>
/// Redis配置节点
/// </summary>
public class RedisConfiguration : ConfigurationSection
{
//<configSections>
// <section name = "redisConfiguration" type="HB.Common.Caches.Configs.RedisConfiguration, HB.Common.Caches.Configs" />
//</configSections>
//<redisConfiguration configSource = "Config\redisConfiguration.config" /> //<redisConfiguration>
// <!--连接字符串-->
// <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" />
//</redisConfiguration> /// <summary>
/// 节点名称
/// </summary>
private const string sectionName = "redisConfiguration"; /// <summary>
/// 获取配置信息
/// </summary>
/// <returns></returns>
public static RedisConfiguration ConfigSection()
{
return (RedisConfiguration)ConfigurationManager.GetSection(sectionName);
} /// <summary>
/// StackExchange配置节点
/// </summary>
[ConfigurationProperty("stackExchangeConnectionStrings", IsRequired = false)]
public StackExchangeConfigurationSection StackExchangeConnectionStrings
{
get
{
return (StackExchangeConfigurationSection)base["stackExchangeConnectionStrings"];
}
set
{
base["stackExchangeConnectionStrings"] = value;
}
} /// <summary>
/// ServiceStack配置节点
/// </summary>
[ConfigurationProperty("serviceStackConnectionStrings", IsRequired = false)]
public ServiceStackConfigurationSection ServiceStackConnectionStrings
{
get
{
return (ServiceStackConfigurationSection)base["serviceStackConnectionStrings"];
}
set
{
base["serviceStackConnectionStrings"] = value;
}
}
}
}
RedisConfiguration.cs
ServiceStackConfigurationSection.cs:ServiceStack插件链接字符串配置项
using System.Configuration; namespace HB.Common.Caches.Configs
{
/// <summary>
/// ServiceStack配置节点
/// </summary>
public class ServiceStackConfigurationSection : ConfigurationElement
{
//<!--连接字符串-->
//<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" /> /// <summary>
/// 可写的Redis链接地址
/// </summary>
[ConfigurationProperty("writehost", IsRequired = false)]
public string WriteHost
{
get
{
return (string)base["writehost"];
}
set
{
base["writehost"] = value;
}
} /// <summary>
/// 可读的Redis链接地址
/// </summary>
[ConfigurationProperty("readhost", IsRequired = false)]
public string ReadHost
{
get
{
return (string)base["readhost"];
}
set
{
base["readhost"] = value;
}
} /// <summary>
/// 登陆密码
/// </summary>
[ConfigurationProperty("password", IsRequired = false)]
public string Password
{
get
{
return (string)base["password"];
}
set
{
base["password"] = value;
}
} /// <summary>
/// 最大写链接数
/// </summary>
[ConfigurationProperty("maxwritepoolsize", IsRequired = false, DefaultValue = )]
public int MaxWritePoolSize
{
get
{
return (int)base["maxwritepoolsize"];
}
set
{
base["maxwritepoolsize"] = value;
}
} /// <summary>
/// 最大读链接数
/// </summary>
[ConfigurationProperty("maxreadpoolsize", IsRequired = false, DefaultValue = )]
public int MaxReadPoolSize
{
get
{
return (int)base["maxreadpoolsize"];
}
set
{
base["maxreadpoolsize"] = value;
}
} /// <summary>
///
/// </summary>
[ConfigurationProperty("autoStart", IsRequired = false, DefaultValue = true)]
public bool AutoStart
{
get
{
return (bool)base["autoStart"];
}
set
{
base["autoStart"] = value;
}
}
}
}
ServiceStackConfigurationSection.cs
StackExchangeConfigurationSection.cs:StackExchange.Redis的链接字符串属性节点
using System.Configuration; namespace HB.Common.Caches.Configs
{
/// <summary>
/// StackExchange节点配置
/// </summary>
public class StackExchangeConfigurationSection : ConfigurationElement
{
// <!--连接字符串-->
// <connectionStrings host = "192.168.3.11:6379,192.168.3.11:6380" password="ChinaNet910111" /> /// <summary>
/// 监听主机
/// </summary>
[ConfigurationProperty("host", IsRequired = true)]
public string Host
{
get
{
return (string)base["host"];
}
set
{
base["host"] = value;
}
} /// <summary>
/// 密码
/// </summary>
[ConfigurationProperty("password", IsRequired = false)]
public string Password
{
get
{
return (string)base["password"];
}
set
{
base["password"] = value;
}
}
}
}
StackExchangeConfigurationSection.cs
业务扩展类:
ServiceStackManager.cs:该管理类ServiceStack的扩展,可以对Redis进行增、删、改、查等一系列操作,具体实现如下
using HB.Common.Caches.Configs;
using ServiceStack.Redis; namespace HB.Common.Caches.Redis
{
/// <summary>
/// Redis客户端管理类
/// </summary>
public class ServiceStackManager
{
/// <summary>
/// 线程同步变量
/// </summary>
private static object synObj = new object(); /// <summary>
/// redis链接池管理对象
/// </summary>
private static PooledRedisClientManager _instance = null; /// <summary>
/// 链接字符串
/// </summary>
private static ServiceStackConfigurationSection ConnectionStrings
{
get
{
var configSection = RedisConfiguration.ConfigSection();
return configSection.ServiceStackConnectionStrings;
}
} /// <summary>
/// 私有构造函数,静止外部通过new关键字来创建该对象实例
/// </summary>
private ServiceStackManager()
{ } /// <summary>
/// 获取redis链接池管理对象实例
/// 实例发生变化的集中情况:
/// 1.实例为空
/// 2.配置文件发生变化
/// </summary>
private static PooledRedisClientManager GetInstance()
{
if (_instance == null)
{
lock (synObj)
{
if (_instance == null)
{
var managerConfig = new RedisClientManagerConfig()
{
AutoStart = ConnectionStrings.AutoStart,
MaxReadPoolSize = ConnectionStrings.MaxReadPoolSize,
MaxWritePoolSize = ConnectionStrings.MaxWritePoolSize
}; string[] readServerList = GetHostList(ConnectionStrings.ReadHost);
string[] writeServerList = GetHostList(ConnectionStrings.WriteHost);
_instance = new PooledRedisClientManager(readServerList, writeServerList, managerConfig);
}
}
}
return _instance;
} /// <summary>
/// 客户端缓存操作对象实例
/// </summary>
public static IRedisClient GetClient(int dbIndex = )
{
var clientManager = GetInstance();
var client = clientManager.GetClient();
if (!string.IsNullOrEmpty(ConnectionStrings.Password))
client.Password = ConnectionStrings.Password; client.Db = dbIndex;
return client;
} /// <summary>
///
/// </summary>
/// <param name="hostStrings"></param>
/// <returns></returns>
private static string[] GetHostList(string hostStrings)
{
if (string.IsNullOrWhiteSpace(hostStrings))
return new string[] { }; return hostStrings.Split(',');
}
}
}
ServiceStackManager.cs
StackExchangeManager.cs:该管理类是对StackExchange.Redis增删改查等操作的扩展
using HB.Common.Caches.Configs;
using log4net;
using Newtonsoft.Json;
using StackExchange.Redis; namespace HB.Common.Caches.Redis
{
/// <summary>
/// StackExchangeManager
///
/// 在StackExchange.Redis中最重要的对象是ConnectionMultiplexer类, 它存在于StackExchange.Redis命名空间中。
/// 这个类隐藏了Redis服务的操作细节,ConnectionMultiplexer类做了很多东西, 在所有调用之间它被设计为共享和重用的。
/// 不应该为每一个操作都创建一个ConnectionMultiplexer 。 ConnectionMultiplexer是线程安全的 , 推荐使用下面的方法。
/// 在所有后续示例中 , 都假定你已经实例化好了一个ConnectionMultiplexer类,它将会一直被重用 ,
/// 现在我们来创建一个ConnectionMultiplexer实例。它是通过ConnectionMultiplexer.Connect 或者 ConnectionMultiplexer.ConnectAsync,
/// 传递一个连接字符串或者一个ConfigurationOptions 对象来创建的。
/// 连接字符串可以是以逗号分割的多个服务的节点.
///
/// 注意 :
/// ConnectionMultiplexer 实现了IDisposable接口当我们不再需要是可以将其释放的 , 这里我故意不使用 using 来释放他。
/// 简单来讲创建一个ConnectionMultiplexer是十分昂贵的 , 一个好的主意是我们一直重用一个ConnectionMultiplexer对象。
/// 一个复杂的的场景中可能包含有主从复制 , 对于这种情况,只需要指定所有地址在连接字符串中(它将会自动识别出主服务器)
/// ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:6379,server2:6379");
/// 假设这里找到了两台主服务器,将会对两台服务进行裁决选出一台作为主服务器来解决这个问题 , 这种情况是非常罕见的 ,我们也应该避免这种情况的发生。
///
///
/// 这里有个和 ServiceStack.Redis 大的区别是没有默认的连接池管理了。没有连接池自然有其利弊,最大的好处在于等待获取连接的等待时间没有了,
/// 也不会因为连接池里面的连接由于没有正确释放等原因导致无限等待而处于死锁状态。缺点在于一些低质量的代码可能导致服务器资源耗尽。不过提供连接池等阻塞和等待的手段是和作者的设计理念相违背的。StackExchange.Redis这里使用管道和多路复用的技术来实现减少连接
///
/// 参考:http://www.cnblogs.com/Leo_wl/p/4968537.html
/// </summary>
public class StackExchangeManager
{
/// <summary>
/// 线程同步变量
/// </summary>
private static object syncObj = new object(); /// <summary>
/// redis链接池管理对象
/// </summary>
private static ConnectionMultiplexer _instance = null; /// <summary>
/// 日志记录器
/// </summary>
private static readonly ILog _log = LogManager.GetLogger(typeof(StackExchangeManager)); /// <summary>
/// 私有构造函数,限制不允许通过new 来实例化该对象
/// </summary>
private StackExchangeManager()
{ } /// <summary>
/// 使用一个静态属性来返回已连接的实例
/// 实例发生变化的几种情况:
/// 1.实例为空
/// 2.连接关闭
/// 3.文件发生变化时
/// </summary>
private static ConnectionMultiplexer GetInstance()
{
if (_instance == null || !_instance.IsConnected)
{
lock (syncObj)
{
if (_instance == null || !_instance.IsConnected)
{
var configSection = RedisConfiguration.ConfigSection();
var stackExchangeConnectionStrings = configSection.StackExchangeConnectionStrings;
_instance = ConnectionMultiplexer.Connect(stackExchangeConnectionStrings.Host);
}
}
}
_instance.ErrorMessage += MuxerErrorMessage;
_instance.HashSlotMoved += MuxerHashSlotMoved;
_instance.InternalError += MuxerInternalError;
_instance.ConnectionFailed += MuxerConnectionFailed;
_instance.ConnectionRestored += MuxerConnectionRestored;
_instance.ConfigurationChanged += MuxerConfigurationChanged;
return _instance;
} /// <summary>
/// 客户端缓存操作对象实例
/// <paramref name="dbIndex"/>
/// </summary>
public static IDatabase GetDatabase(int dbIndex = -)
{
var instance = GetInstance();
return instance.GetDatabase(dbIndex);
} /// <summary>
/// 配置更改时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void MuxerConfigurationChanged(object sender, EndPointEventArgs e)
{
_log.Warn($"Muxer Configuration Changed=>EndPoint:{JsonConvert.SerializeObject(e.EndPoint)}");
} /// <summary>
/// 发生错误时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void MuxerErrorMessage(object sender, RedisErrorEventArgs e)
{
_log.Error($"Muxer ErrorMessage=>RedisErrorEventArgs:{JsonConvert.SerializeObject(e)}");
} /// <summary>
/// 重新建立连接之前的错误
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e)
{
_log.Error($"Muxer Connection Restored=>ConnectionFailedEventArgs:{JsonConvert.SerializeObject(e)}");
} /// <summary>
/// 连接失败 , 如果重新连接成功你将不会收到这个通知
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e)
{
_log.Error($"Muxer Connection Failed=>ConnectionFailedEventArgs:{JsonConvert.SerializeObject(e)}");
} /// <summary>
/// 更改集群
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e)
{
_log.Error($"Muxer HashSlot Moved=>HashSlotMovedEventArgs:{JsonConvert.SerializeObject(e)}");
} /// <summary>
/// redis类库错误
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void MuxerInternalError(object sender, InternalErrorEventArgs e)
{
_log.Error($"Muxer Internal Error=>InternalErrorEventArgs:{JsonConvert.SerializeObject(e)}");
}
}
}
StackExchangeManager.cs
RedisSessionManager.cs:主要是StackExchange.Redis插件实现的用户登陆会话管理类
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Web; namespace HB.Common.Caches.Redis
{
/// <summary>
/// Redis Session 管理类
/// </summary>
public class RedisSessionManager
{
/// <summary>
/// 获取并设置在会话状态提供程序终止会话之前各请求之间所允许的时间(以分钟为单位)
/// </summary>
public int TimeOut { get; set; } /// <summary>
/// 获取一个值,该值指示会话是否为只读
/// </summary>
public bool IsReadOnly { get; set; } /// <summary>
/// HttpContext
/// </summary>
private HttpContextBase HttpContext { get; set; } /// <summary>
/// SessionId标识符
/// </summary>
public static string SessionName = "HB.Redis.SessionId"; /// <summary>
/// 缓存
/// </summary>
private static IDatabase _cache = StackExchangeManager.GetDatabase(); /// <summary>
/// 获取会话状态集合中的项数
/// </summary>
public long Count
{
get
{
return _cache.HashLength(SessionId);
}
} /// <summary>
/// 静止无参的构造函数被实例化
/// </summary>
private RedisSessionManager() { } /// <summary>
/// 构造函数
/// </summary>
/// <param name="httpRequst">客户端的请求信息</param>
/// <param name="isReadOnly">
/// 获取一个值,该值指示会话是否为只读
/// 说明:true 表示只读;false 表示读写
/// 默认:true
/// </param>
/// <param name="timeout">
/// 会话有效期
/// 单位:分钟
/// 默认:20分钟
/// </param>
public RedisSessionManager(HttpContextBase httpContext, bool isReadOnly = true, int timeout = )
{
TimeOut = timeout;
IsReadOnly = isReadOnly;
HttpContext = httpContext;
if (_cache.KeyExists(SessionId))
{
//设置缓存有效期(第二次以上通过這里更新缓存有效期)
_cache.KeyExpire(SessionId, expiry: DateTime.Now.AddMinutes(TimeOut));
}
} /// <summary>
/// 获取并设置在会话状态提供程序终止会话之前各请求之间所允许的时间(以分钟为单位)
/// </summary>
/// <returns></returns>
public string SessionId
{
get
{
var cookie = HttpContext.Request.Cookies.Get(SessionName);
if (cookie == null || string.IsNullOrEmpty(cookie.Value))
{
string newSessionID = Guid.NewGuid().ToString();
var newCookie = new HttpCookie(SessionName, newSessionID);
newCookie.HttpOnly = IsReadOnly;
newCookie.Expires = DateTime.Now.AddMinutes(TimeOut);
HttpContext.Response.Cookies.Add(newCookie);
return newSessionID;
}
else
{
return cookie.Value;
}
}
} /// <summary>
/// 按名称获取或者设置会话值
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public object this[string name]
{
get
{
return _cache.HashGet(SessionId, name);
}
set
{
var isKeyExists = _cache.KeyExists(SessionId);//缓存是否存在
var hashValue = JsonConvert.SerializeObject(value);
var isSuccess = _cache.HashSet(SessionId, name, hashValue);
if (isSuccess && !isKeyExists)
{
//设置缓存有效期(第一次插入缓存的时候通过這里设置缓存的有效期)
_cache.KeyExpire(SessionId, expiry: DateTime.Now.AddMinutes(TimeOut));
}
}
} /// <summary>
/// 判断会话中是否存在指定key
/// </summary>
/// <param name="name">键值</param>
/// <returns></returns>
public bool Exists(string name)
{
return _cache.HashExists(SessionId, name);
} /// <summary>
/// 从会话集合中移除所有的键值
/// </summary>
/// <returns></returns>
public bool Clear()
{
return _cache.KeyDelete(SessionId);
} /// <summary>
/// 删除会话状态集合中的指定项
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public bool Remover(string name)
{
return _cache.HashDelete(SessionId, name);
}
}
}
RedisSessionManager.cs
有需要源码的同学,可以给我留言或者发到我得邮箱。