ServiceStack.Redis 学习笔记

开源版本: 4.001m;

          一般调用方法:

    初始定义 连接池,以下简称pool

   public static class Redis
    {

        public static PooledRedisClientManager redisManager = null;
        private static object lockobj = new object();
        private static List errorLog = new List();

        private static PooledRedisClientManager CreateManager()
        {
            List list = ConfigurationBroker.Instance("").Get();
            ServiceElement ServiceElement = list[0];
            List IPlist = new List();
            for (int i = 0; i < list.Count; i++)
            {
                IPlist.Add(list[i].IP);
            }
            
            PooledRedisClientManager manager = new PooledRedisClientManager(IPlist, null,
                new RedisClientManagerConfig
                {
                    MaxWritePoolSize = ServiceElement.MaxWritePoolSize,
                    MaxReadPoolSize = ServiceElement.MaxReadPoolSize,
                    AutoStart = true,
                });
           
            return manager;
        }

        static Redis()
        {
            if (redisManager == null)
            {
                lock (lockobj)
                {
                    if (redisManager == null)
                    {
                        redisManager = CreateManager(); //初始化Redis客户端
                    }
                }
            }
        }
        /// 
        /// 获取string类型数据
        /// 
        /// key
        /// 
        public static string GetString(string key)
        {
            using (RedisClient client = (RedisClient)redisManager.GetClient())
            {
                string value = null;
                try
                {
                    byte[] bytes = client.Get(key);
                    if (bytes != null)
                    {
                        value = bytes.FromUtf8Bytes();
                    }
                    return value;
                }
                catch
                {
                    return null;
                }
            }
        }
        private static string FromUtf8Bytes(this byte[] bytes)
        {
            if (bytes != null)
            {
                return System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
            }
            return null;
        }
        /// 
        ///  设置为string类型用,原生的数据(泛型中的方法要转换为json)
        /// 
        /// key
        /// 值(字符串)
        /// 过期时长
        /// 
        public static bool SetString(string key, string value, TimeSpan expiresIn)
        {    
            using (RedisClient client = redisManager.GetClient() as RedisClient)
            {
                try
                {
                    client.SetEntry(key, value, expiresIn);
                    return true;
                }
                catch 
                { 
                    return false;
                }
            }
        }
        public static bool SetString(string key, string value)
        {
            using (RedisClient client = redisManager.GetClient() as RedisClient)
            {
                try
                {
                    client.SetEntry(key, value);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
        }
    }


 
  

  PooledRedisClientManager.cs 代码分析:

属性介绍:

PoolTimeout  获取一个客户端超时设置   可以在初始化pool后直接赋值,也可以在初始化时赋值。

                     但是初始化的时候赋值,poolTimeOutSeconds 必须以秒为单位,内部其实是 毫秒单位。默认超时 2秒。

                   初始化赋值源码:

                     this.PoolTimeout = poolTimeOutSeconds != null                ? poolTimeOutSeconds * 1000                : 2000; //Default Timeout

ConnectTimeout:pool的一个属性;Socket链接超时设置,单位毫秒。初始化pool后可以直接对pool赋值;

SocketSendTimeout:pool的一个属性;Socket发送命令超时设置,单位毫秒;初始化pool后可以直接对pool赋值;

SocketReceiveTimeout:pool的一个属性;Socket接受命令超时设置,单位毫秒;初始化pool后可以直接对pool赋值;

RedisClientCounter:RedisClient被创建的No值,从0开始。

Active:RedisClient的一个属性;true:使用中,false:闲置;

RecheckPoolAfterMs:从pool获取客户端时,如果全部客户端都在使用,等待RecheckPoolAfterMs毫秒,重新while;

WritePoolIndex:pool获取客户端的次数累计,主要用于从列表中获取客户端的时候,尽量做到平均。

RedisClientCounter:RedisClient被创建的No值,一致累计存在RedisClient.Id里面 ,从0开始。

ReadWriteHosts:pool配置信息,可读写的客户端IP,端口列表对象。

ReadOnlyHosts:pool配置信息,只读的客户端IP,端口列表对象。

RedisClientFactory:RedisClient创建工厂对象。

readClients:只读操作客户端列表 ;RedisClient类型,内部主要为Socket,以及一些附属状态。

writeClients:读写操作客户端列表 ;RedisClient类型,内部主要为Socket,以及一些附属状态。

方法:

FailoverTo:灾难切换:这里他只是把 readClients、writeClients强行清空,  重新对ReadWriteHosts、ReadOnlyHosts复制。

                    此方法,必须主动调用,客户端没有检查和灾难切换机制。

                  

GetClient:获取一个RedisClient 实例;通过   lock (writeClients) 防止 多个线程获取同一个RedisClient;

                     1、通过 while调用GetInActiveWriteClient 方法来获取客户端;

                     2、把   一些超时设置传给RedisClient,让他再起Socket的时候用。

                    3、设置Action=true

GetInActiveWriteClient:获取一个客户端:这里作者非常巧妙的 通过 ReadWriteHosts.Count和WritePoolIndex,轮询获取RedisClient。

                                      WritePoolIndex 仅仅一个计数器,在获取RedisClient时候用到。

   获取完成后把 自己(pool)传给客户端,主要是在用完释放的时候修改Action属性。【  client.ClientManager = this;】

   源码:

  

// 
        /// Called within a lock
        /// 
        /// 
        private RedisClient GetInActiveWriteClient()
        {
            
            var desiredIndex = WritePoolIndex % writeClients.Length;
            //var  desiredIndex = index >writeClients.Length-1 ?
            //this will loop through all hosts in readClients once even though there are 2 for loops
            //both loops are used to try to get the prefered host according to the round robin algorithm
            for (int x = 0; x < ReadWriteHosts.Count; x++)
            {
                var nextHostIndex = (desiredIndex + x) % ReadWriteHosts.Count;
                var nextHost = ReadWriteHosts[nextHostIndex];
                for (var i = nextHostIndex; i < writeClients.Length; i += ReadWriteHosts.Count)
                {
                    if (writeClients[i] != null && !writeClients[i].Active && !writeClients[i].HadExceptions){
                            return writeClients[i];
                    }
                    
                    else if (writeClients[i] == null || writeClients[i].HadExceptions)
                    {
                        if (writeClients[i] != null)
                            writeClients[i].DisposeConnection();
                        var client = RedisClientFactory.CreateRedisClient(nextHost.Host, nextHost.Port);


                        if (nextHost.RequiresAuth)
                            client.Password = nextHost.Password;
                        client.Id = RedisClientCounter++;
                        client.ClientManager = this;
                        client.NamespacePrefix = NamespacePrefix;
                        client.ConnectionFilter = ConnectionFilter;
                        client.ClientManagerWriteHostHashCode = this.ReadWriteHostsHashCode;
                        writeClients[i] = client;


                        return client;
                    }
                }
            }
            return null;
        }

  假如你有 A,B两个Redis端口。最多创建10个链接;

程序起来,

第一次:获取的时候writeClients 全部为空,RedisClientFactory创建一个对象放到 writeClients[0]里面,那么重建的第一个对象就是 A的链接。

     假如每次使用都能很快释放对象。

第二次:获取到B端口的一个RedisClient,同事存放到writeClients[1]里面。


假如程序处理都很快。最多会创建 两个writeClients,就是两个Socket 连接。

假如程序处理的比较慢,第三个人来获取RedisClient的时候,1,2都在使用,pool会再创建一个A端口的链接放到 writeClients[3]里,以此类推。

最后会是A,B端口各有五个RedisClient(Socket)连接。

RedisClient的获取 和操作 Set,Get没有任何关系,Set,Get都是RedisClient的一个方法。


代码使用分析:

 using (RedisClient client = redisManager.GetClient() as RedisClient)

{

      byte[] bytes = client.Get(key);

}

1、其中using作用:用完调用对象 RedisClien的Dispose方法;RedisClien的Dispose方法

会判断Socket链接情况,并 Action=alse;


2、RedisClient client = redisManager.GetClient() as RedisClien 这句话,只是创建了一个 外壳。这里根本没有new Socket。

    这有在做client.Get(key); 的时候,内部才会去new Socket,并把这个Socket把存起来,

    

socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) {
                SendTimeout = SendTimeout,
                ReceiveTimeout = ReceiveTimeout








你可能感兴趣的:(ServiceStack.Redis 学习笔记)