Redis进阶实践之九 独立封装的RedisClient客户端工具类
阅读原文时间:2024年06月19日阅读:1

**一、引言
**

今天开始有关Redis学习的第九篇文章了,以后肯定会大量系统使用Redis作为缓存介质,为了更好的更好的Redis,自己写了两个工具类,但是这两个工具类,没有提供一致的接口,是为了使用的独立性。测试已经完毕,可以正常访问Windows和Linux版本上的Redis服务,各种操作也没问题。今天就把主要代码贴出来,因为有一部分是需要配置文件的,我自己独立写了一套配置系统,这套配置系统就不贴出来,大家可以根据自己的理解来写这个部分的内容,好了,开始我们今天的写作吧。

**二、简介
**

我写了两个工具类,第一个是以【StackExchange.Redis】为实现技术的Redis的客户端工具类,另一个是以【ServiceStack.Redis】为实现技术的Redis客户端工具类。【ServiceStack.Redis】的官网主页:https://servicestack.net/redis,【ServiceStack.Redis】在github上的主页:https://github.com/ServiceStack/ServiceStack.Redis,【ServiceStack.Redis】和Visual Studio 2015整个也很方便,微软在这块做的的很不过,我们可以通过命令直接安装:Install-Package ServiceStack.Redis,当然我们也可以通过NuGet来安装,安装完成,在您项目的【引用】里面会生成四个dll,分别是:ServiceStack.Common,ServiceStack.Interfaces,ServiceStack.Redis,ServiceStack.Client 四个程序集,然后在项目中使用Using引入就可以正常使用了。【ServiceStack.Redis】是Redis官方推荐的C#客户端,性能非常优越,使用也很方便,但是从v4版本已经逐渐商业化了,目的可以想而知,嗨,都是钱惹的祸。

上面我说道了,【ServiceStack.Redis】后来就商业化了,我想了想光靠他也是不行了,还要在写一个版本的工具类,所以就选择了【StackExchange.Redis】,【StackExchange.Redis】也是开源的,到目前,一直都是开源的,它的地址是:https://github.com/StackExchange/StackExchange.Redis,我使用的是.net 4.5,工具采用 vs2015, StackExchange.Redis版本是1.0.488。工具类还会在持续的使用过程中,我还会更新和修改的。

**三、实例代码
** 
         这是Redis客户端的配置文件的格式,格式很简单,可以分开配置,也可以合在一起配置。代码中标红的是和我的配置系统有关的代码,大家请注意。


192.168.3.11:,192.168.3.11: 192.168.131.1:

1、这是以【ServiceStack.Redis】为实现技术的工具类,对外界的访问接口提供了2个,第一个是以配置文件中自定义的名称参数的,红色代码是和我独立的配置系统相关联的,另一个访问接口是以配置实体类参数的,代码很简单,不多说了。

工具类:ServiceStackRedisClientProvider.cs

///

/// 通过ServiceStack.Redis实现的Redis的客户端操作类型 ///
public sealed class ServiceStackRedisClientProvider
{
#region 私有变量

     //线程同步变量  
     private static readonly object lockObject = new object();

     //redis链接池管理对象  
     private static volatile PooledRedisClientManager \_instance = null;

     //配置文件里面的ServiceStack详细配置设置  
     private static ServiceStackDetails \_serviceStackDetails;

     //可以自行配置ServiceStack的配置对象  
     private static ServiceStackConfigEntry \_serviceStackConfigEntry;

     #endregion

     #region 私有构造函数

     /// <summary>  
     /// 私有构造函数,禁止外部通过new关键字来创建该对象实例  
     /// </summary>  
     private ServiceStackRedisClientProvider() { }

     #endregion

     #region 获取PooledRedisClientManager实例的方法

     /// <summary>  
     /// 获取redis链接池管理对象实例  
     /// 实例发生变化的集中情况:  
     /// 1.实例为空  
     /// 2.配置文件发生变化  
     /// </summary>  
     /// <param name="startByConfigFile">这是一个布尔值,true表示根据配置文件的配置启动,false表示是根据配置对象启动</param>  
     /// <returns>返回PooledRedisClientManager类型的对象实例</returns>  
     private static PooledRedisClientManager GetInstance(bool startByConfigFile)  
     {  
         if (\_instance == null)  
         {  
             lock (lockObject)  
             {  
                 if (\_instance == null)  
                 {  
                     string\[\] readWriteServerList=null;  
                     string\[\] readOnlyServerList=null;  
                     RedisClientManagerConfig managerConfig=null;

                     //根据我们配置文件中数据来设置启动信息(app.config或者web.config)  
                     if (startByConfigFile && (\_serviceStackDetails != null))  
                     {  
                         managerConfig = new RedisClientManagerConfig()  
                         {  
                             AutoStart = \_serviceStackDetails.AutoStart,  
                             MaxReadPoolSize = \_serviceStackDetails.MaxReadPoolSize,  
                             MaxWritePoolSize = \_serviceStackDetails.MaxWritePoolSize,  
                         };

                         readWriteServerList = GetRedisHosts(\_serviceStackDetails.ReadWriteHosts);  
                         readOnlyServerList = GetRedisHosts(\_serviceStackDetails.ReadOnlyHosts);  
                     }  
                     else if (!startByConfigFile && (\_serviceStackConfigEntry != null))//根据配置对象来设置启动信息(ServiceStackConfigEntry)  
                     {  
                         managerConfig = new RedisClientManagerConfig()  
                         {  
                             AutoStart = \_serviceStackConfigEntry.AutoStart,  
                             MaxReadPoolSize = \_serviceStackConfigEntry.MaxReadPoolSize,  
                             MaxWritePoolSize = \_serviceStackConfigEntry.MaxWritePoolSize,  
                         };

                         readWriteServerList = GetRedisHosts(\_serviceStackConfigEntry.ReadWriteHosts);  
                         readOnlyServerList = GetRedisHosts(\_serviceStackConfigEntry.ReadOnlyHosts);  
                     }  
                     else  
                     {  
                         throw new InvalidOperationException("Redis客户端初始化配置失败!");  
                     }

                     if ((readWriteServerList != null && readWriteServerList.Length > )&&(readOnlyServerList != null && readOnlyServerList.Length <= ))  
                     {  
                         \_instance = new PooledRedisClientManager(readWriteServerList);  
                     }

                     if ((readWriteServerList != null && readWriteServerList.Length > ) && (readOnlyServerList != null && readOnlyServerList.Length > ))  
                     {  
                         \_instance = new PooledRedisClientManager(readWriteServerList, readOnlyServerList, managerConfig);  
                     }  
                 }  
             }  
         }  
         return \_instance;  
     }

     /// <summary>  
     /// 解析Redis服务器列表,该列表格式IP\[:Port\]  
     /// </summary>  
     /// <param name="redisHosts">包含一个或者多个Redis服务器地址的字符串列表,以逗号做为分隔符</param>  
     /// <returns>返回Redis服务器地址列表</returns>  
     private static string\[\] GetRedisHosts(string redisHosts)  
     {  
         if (string.IsNullOrWhiteSpace(redisHosts) || string.IsNullOrEmpty(redisHosts))  
         {  
             return new string\[\] { };  
         }  
         var hosts=redisHosts.Split(',');  
         foreach (var host in hosts)  
         {  
             if (!Regex.IsMatch(host, @"^(25\[0-5\]|2\[0-4\]\[0-9\]|\[0-1\]{1}\[0-9\]{2}|\[1-9\]{1}\[0-9\]{1}|\[1-9\])\\.(25\[0-5\]|2\[0-4\]\[0-9\]|\[0-1\]{1}\[0-9\]{2}|\[1-9\]{1}\[0-9\]{1}|\[1-9\]|0)\\.(25\[0-5\]|2\[0-4\]\[0-9\]|\[0-1\]{1}\[0-9\]{2}|\[1-9\]{1}\[0-9\]{1}|\[1-9\]|0)\\.(25\[0-5\]|2\[0-4\]\[0-9\]|\[0-1\]{1}\[0-9\]{2}|\[1-9\]{1}\[0-9\]{1}|\[0-9\]):\\d{3,4}$"))  
             {  
                 throw new InvalidOperationException("Redis服务器地址格式不正确!");  
             }  
         }  
         return hosts;  
     }

     #endregion

     #region 提供对外访问接口

     /// <summary>  
     /// 获取Redis客户端对象实例  
     /// </summary>  
     /// <param name="redisClientName">在配置文件中,Redis客户端的名称</param>  
     /// <param name="databaseIndex">redis逻辑分为16个数据库,排序为:0-15,我们默认使用的是0号数据库,数据库当前的索引值</param>  
     /// <returns>返回IRedisClient对象实例</returns>  
     public static IRedisClient GetRedisClient(string redisClientName,int databaseIndex = )  
     {  
         //获取配置数据  
         ParameterValidityChecker.RequiredParameterStringNotNullOrWhiteSpace(redisClientName, "Redis客户端的名称不能为空!");  
         var \_configurationManager = (ConfigurationFrameworkManager)ConfigurationManager.GetSection("Framework");  
         if (\_configurationManager != null)  
         {  
             \_serviceStackDetails = \_configurationManager.RedisClientConfiguration.GetServiceStackDetails(redisClientName);  
             if (\_serviceStackDetails == null)  
             {  
                 throw new InvalidOperationException("以ServiceStack.Redis为实现技术的Redis客户端的配置有误!");  
             }  
         }  
         else  
         {  
             throw new InvalidOperationException("以ServiceStack.Redis为实现技术的Redis客户端的配置有误!");  
         }

         //实例化Redis客户端实例对象  
         var pooledRedisClientManager = GetInstance(true);  
         var redisClient = pooledRedisClientManager.GetClient();  
         if (!string.IsNullOrEmpty(\_serviceStackDetails.Password))  
         {  
             redisClient.Password = \_serviceStackDetails.Password;  
         }  
         redisClient.Db = databaseIndex;  
         return redisClient;  
     }

     /// <summary>  
     /// 获取Redis客户端对象实例  
     /// </summary>  
     /// <param name="serviceStackConfigEntry">在配置文件中,Redis客户端的名称</param>  
     /// <param name="databaseIndex">redis逻辑分为16个数据库,排序为:0-15,我们默认使用的是0号数据库,数据库当前的索引值</param>  
     /// <returns>返回IRedisClient对象实例</returns>  
     public static IRedisClient GetRedisClient(ServiceStackConfigEntry serviceStackConfigEntry, int databaseIndex = )  
     {  
         //获取配置数据  
         if (serviceStackConfigEntry == null)  
         {  
             throw new ArgumentNullException("以ServiceStack.Redis为实现技术的Redis客户端的配置对象不能为空!");  
         }  
         else  
         {  
             \_serviceStackConfigEntry = serviceStackConfigEntry;  
         }

         if (string.IsNullOrEmpty(\_serviceStackConfigEntry.ReadWriteHosts) || string.IsNullOrWhiteSpace(\_serviceStackConfigEntry.ReadWriteHosts))  
         {  
             throw new InvalidOperationException("【ReadWriteHosts】必须设置其值!");  
         }

         //实例化Redis客户端实例对象  
         var pooledRedisClientManager = GetInstance(false);  
         var redisClient = pooledRedisClientManager.GetClient();  
         if (!string.IsNullOrEmpty(\_serviceStackConfigEntry.Password)&&!string.IsNullOrWhiteSpace(\_serviceStackConfigEntry.Password))  
         {  
             redisClient.Password = \_serviceStackConfigEntry.Password;  
         }  
         redisClient.Db = databaseIndex;  
         return redisClient;  
     }

     #endregion  
 }

配置实体类 ServiceStackConfigEntry.cs的代码:

///

/// 配置文件中,以ServiceStack.Redis为实现技术的配置Redis的详情 ///
public sealed class ServiceStackConfigEntry
{
#region 构造函数

     /// <summary>  
     /// 给配置参数初始化默认值  
     /// </summary>  
     public ServiceStackConfigEntry()  
     {  
         ReadWriteHosts = "127.0.0.1:6379";  
         ReadOnlyHosts = string.Empty;  
         MaxWritePoolSize = ;  
         MaxReadPoolSize = ;  
         Password = string.Empty;  
         AutoStart = true;  
     }

     #endregion

     #region 配置属性

     /// <summary>  
     /// 可读可写的Redis服务器地址,多个地址以逗号分隔,例如:192.168.127.11:6379,192.168.127.128:6380  
     /// </summary>  
     public string ReadWriteHosts { get; set; }

     /// <summary>  
     /// 只能读的Redis服务器地址,多个地址以逗号分隔,例如:192.168.127.11:6379,192.168.127.128:6380  
     /// </summary>  
     public string ReadOnlyHosts { get; set; }

     /// <summary>  
     /// 最大写链接数  
     /// </summary>  
     public int MaxWritePoolSize { get; set; }

     /// <summary>  
     /// 最大读链接数  
     /// </summary>  
     public int MaxReadPoolSize { get; set; }

     /// <summary>  
     /// 登陆Redis服务器的密码  
     /// </summary>  
     public string Password { get; set; }

     /// <summary>  
     /// 是否自动启动  
     /// </summary>  
     public bool AutoStart { get; set; }

     #endregion  
 }

获取配置文件详情的类型:ServiceStackDetails.cs

///

/// 配置文件中,以ServiceStack.Redis为实现技术的配置Redis的详情 ///
public sealed class ServiceStackDetails
{
#region 构造函数

    /// <summary>  
    /// 给配置参数初始化默认值  
    /// </summary>  
    public ServiceStackDetails()  
    {  
        ReadWriteHosts = "127.0.0.1:6379";  
        ReadOnlyHosts = string.Empty;  
        MaxWritePoolSize = ;  
        MaxReadPoolSize = ;  
        Password = string.Empty;  
        AutoStart = true;  
    }

    #endregion

    #region 配置属性

    /// <summary>  
    /// 可读可写的Redis服务器地址,多个地址以逗号分隔,例如:192.168.127.11:6379,192.168.127.128:6380  
    /// </summary>  
    public string ReadWriteHosts { get; internal set; }

    /// <summary>  
    /// 只能读的Redis服务器地址,多个地址以逗号分隔,例如:192.168.127.11:6379,192.168.127.128:6380  
    /// </summary>  
    public string ReadOnlyHosts { get; internal set; }

    /// <summary>  
    /// 最大写链接数  
    /// </summary>  
    public int MaxWritePoolSize { get; internal set; }

    /// <summary>  
    /// 最大读链接数  
    /// </summary>  
    public int MaxReadPoolSize { get; internal set; }

    /// <summary>  
    /// 登陆Redis服务器的密码  
    /// </summary>  
    public string Password { get; internal set; }

    /// <summary>  
    /// 是否自动启动  
    /// </summary>  
    public bool AutoStart { get; internal set; }

    #endregion  
}

2、这是以【StackExchange.Redis】为实现技术的工具类,对外界的访问接口提供了2个,第一个是以配置文件中自定义的名称参数的,红色代码是和我独立的配置系统相关联的,另一个访问接口是以配置实体类参数的,代码很简单,不多说了。

工具类: StackExchangeRedisClientProvider.cs

///

/// 通过StackExchange.Redis实现的Redis的客户端操作类型 ///
public sealed class StackExchangeRedisClientProvider
{
#region 私有字段

     /// <summary>  
     /// 线程同步变量  
     /// </summary>  
     private static readonly object lockObject = new object();

     /// <summary>  
     /// redis链接池管理对象  
     /// </summary>  
     private static volatile ConnectionMultiplexer \_instance;

     /// <summary>  
     /// 日志记录器  
     /// </summary>  
     private static readonly ILog \_log = LogManager.GetLogger(typeof(StackExchangeRedisClientProvider));

     private static StackExchangeDetails \_stackExchangeDetails;

     private static StackExchangeConfigEntry \_stackExchangeConfigEntry;

     #endregion

     #region 私有构造函数

     /// <summary>  
     /// 私有构造函数,禁止不允许通过new 来实例化该对象  
     /// </summary>  
     private StackExchangeRedisClientProvider() { }

     #endregion

     #region 获取Redis客户端实例

     /// <summary>  
     /// 使用一个静态属性来返回已连接的实例  
     /// 实例发生变化的几种情况:  
     /// 1.实例为空  
     /// 2.连接关闭  
     /// 3.文件发生变化时  
     /// </summary>  
     /// <param name="startByConfigFile">这是一个布尔值,true表示根据配置文件的配置启动,false表示是根据配置对象启动</param>  
     /// <returns>返回ConnectionMultiplexer类型的对象实例</returns>  
     private static ConnectionMultiplexer GetInstance(bool startByConfigFile)  
     {  
         if (startByConfigFile)  
         {  
             GetRedisHosts(\_stackExchangeDetails.Hosts);  
         }  
         else  
         {  
             GetRedisHosts(\_stackExchangeConfigEntry.Hosts);  
         }

         if (\_instance == null || !\_instance.IsConnected)  
         {  
             lock (lockObject)  
             {  
                 if (\_instance == null || !\_instance.IsConnected)  
                 {  
                     if (startByConfigFile)  
                     {  
                         \_instance = ConnectionMultiplexer.Connect(\_stackExchangeDetails.Hosts);  
                     }  
                     else  
                     {  
                         \_instance = ConnectionMultiplexer.Connect(\_stackExchangeConfigEntry.Hosts);  
                     }  
                 }  
             }  
         }  
         \_instance.ErrorMessage += MuxerErrorMessage;  
         \_instance.HashSlotMoved += MuxerHashSlotMoved;  
         \_instance.InternalError += MuxerInternalError;  
         \_instance.ConnectionFailed += MuxerConnectionFailed;  
         \_instance.ConnectionRestored += MuxerConnectionRestored;  
         \_instance.ConfigurationChanged += MuxerConfigurationChanged;  
         return \_instance;  
     }

     /// <summary>  
     /// 解析Redis服务器列表,该列表格式IP:Port  
     /// </summary>  
     /// <param name="redisHosts">包含一个或者多个Redis服务器地址的字符串列表,以逗号做为分隔符</param>  
     private static void GetRedisHosts(string redisHosts)  
     {  
         if (string.IsNullOrWhiteSpace(redisHosts) || string.IsNullOrEmpty(redisHosts))  
         {  
             return;  
         }  
         var hosts = redisHosts.Split(',');  
         foreach (var host in hosts)  
         {  
             if (!Regex.IsMatch(host, @"^(25\[0-5\]|2\[0-4\]\[0-9\]|\[0-1\]{1}\[0-9\]{2}|\[1-9\]{1}\[0-9\]{1}|\[1-9\])\\.(25\[0-5\]|2\[0-4\]\[0-9\]|\[0-1\]{1}\[0-9\]{2}|\[1-9\]{1}\[0-9\]{1}|\[1-9\]|0)\\.(25\[0-5\]|2\[0-4\]\[0-9\]|\[0-1\]{1}\[0-9\]{2}|\[1-9\]{1}\[0-9\]{1}|\[1-9\]|0)\\.(25\[0-5\]|2\[0-4\]\[0-9\]|\[0-1\]{1}\[0-9\]{2}|\[1-9\]{1}\[0-9\]{1}|\[0-9\]):\\d{3,4}$"))  
             {  
                 throw new InvalidOperationException("Redis服务器地址格式不正确!");  
             }  
         }  
     }

     /// <summary>  
     /// 获取Redis客户端对象实例  
     /// </summary>  
     /// <param name="redisClientName">在配置文件中,Redis客户端的名称</param>  
     /// <param name="databaseIndex">redis逻辑分为16个数据库,排序为:0-15,我们默认使用的是0号数据库,数据库当前的索引值</param>  
     /// <returns></returns>  
     public static IDatabase GetRedisClient(string redisClientName, int databaseIndex =)  
     {  
         //获取配置数据  
         ParameterValidityChecker.RequiredParameterStringNotNullOrWhiteSpace(redisClientName, "Redis客户端的名称不能为空!");  
         var \_configurationManager = (ConfigurationFrameworkManager)ConfigurationManager.GetSection("Framework");  
         if (\_configurationManager != null)  
         {  
             \_stackExchangeDetails = \_configurationManager.RedisClientConfiguration.GetStackExchangeDetails(redisClientName);  
             if (\_stackExchangeDetails == null)  
             {  
                 throw new InvalidOperationException("以StackExchange.Redis为实现技术的Redis客户端的配置有误!");  
             }  
         }  
         else  
         {  
             throw new InvalidOperationException("以StackExchange.Redis为实现技术的Redis客户端的配置有误!");  
         }

         //实例化Redis客户端实例对象  
         var instance = GetInstance(true);  
         return instance.GetDatabase(databaseIndex);  
     }

     /// <summary>  
     /// 获取Redis客户端对象实例  
     /// </summary>  
     /// <param name="stackExchangeConfigEntry">StackExchange配置对象</param>  
     /// <param name="databaseIndex">redis逻辑分为16个数据库,排序为:0-15,我们默认使用的是0号数据库,数据库当前的索引值</param>  
     /// <returns></returns>  
     public static IDatabase GetRedisClient(StackExchangeConfigEntry stackExchangeConfigEntry, int databaseIndex =)  
     {  
         //获取配置数据  
         if (stackExchangeConfigEntry == null)  
         {  
             throw new ArgumentNullException("以StackExchange.Redis为实现技术的Redis客户端的配置对象不能为空!");  
         }  
         else  
         {  
             \_stackExchangeConfigEntry = stackExchangeConfigEntry;  
         }

         if (string.IsNullOrEmpty(\_stackExchangeConfigEntry.Hosts) || string.IsNullOrWhiteSpace(\_stackExchangeConfigEntry.Hosts))  
         {  
             throw new InvalidOperationException("【Hosts】必须设置其值!");  
         }

         //实例化Redis客户端实例对象  
         var instance = GetInstance(false);  
         return instance.GetDatabase(databaseIndex);  
     }

     #endregion  
 }

配置实体类:StackExchangeConfigEntry.cs

///

/// 配置文件中,以StackExchange.Redis为实现技术的配置Redis的详情 ///
public sealed class StackExchangeConfigEntry
{
#region 构造函数

     /// <summary>  
     /// 给配置参数初始化默认值  
     /// </summary>  
     public StackExchangeConfigEntry()  
     {  
         Hosts = "127.0.0.1:6379";  
         Password = string.Empty;  
     }

     #endregion

     #region 配置属性

     /// <summary>  
     /// Redis服务器地址,多个地址以逗号分隔,例如:192.168.127.11:6379,192.168.127.128:6380  
     /// </summary>  
     public string Hosts { get; set; }

     /// <summary>  
     /// 登陆Redis服务器的密码  
     /// </summary>  
     public string Password { get; set; }

     #endregion  
 }

根据配置信息获取数据的类型:StackExchangeDetails.cs

///

/// 配置文件中,以StackExchange.Redis为实现技术的配置Redis的详情 ///
public sealed class StackExchangeDetails
{
#region 构造函数

     /// <summary>  
     /// 给配置参数初始化默认值  
     /// </summary>  
     public StackExchangeDetails()  
     {  
         Hosts = "127.0.0.1:6379";  
         Password = string.Empty;  
     }

     #endregion

     #region 配置属性

     /// <summary>  
     /// Redis服务器的主机地址,如果多个地址则以逗号分隔,格式:127.0.0.1:6379,127.0.0.1:6380  
     /// </summary>  
     public string Hosts{ get; internal set; }

     /// <summary>  
     /// 登陆Redis服务器的密码  
     /// </summary>  
     public string Password { get; internal set; }

     #endregion  
 }

四、结束

好了,今天就写到这里了,先说明一下,这两个类暂时没有提供统一的接口,看以后的需要吧,如果有需要,我在重构。StackExchangeDetails 和 ServiceStackDetails 这两个类在这个 Enterprise.Framework.Configuration 命名空间,配置的系统暂时就不贴代码了,代码很多,其他的类型都在 Enterprise.Framework.NoSQL.RedisClient 这个命名空间下边。