开源版本: 4.001m;
一般调用方法:
初始定义 连接池,以下简称pool
public static class Redis
{
public static PooledRedisClientManager redisManager = null;
private static object lockobj = new object();
private static List<string> errorLog = new List<string>();
private static PooledRedisClientManager CreateManager()
{
List<ServiceElement> list = ConfigurationBroker.Instance("").Get();
ServiceElement ServiceElement = list[0];
List<string> IPlist = new List<string>();
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客户端
}
}
}
}
/// <summary>
/// 获取string类型数据
/// </summary>
/// <param name="key">key</param>
/// <returns></returns>
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;
}
/// <summary>
/// 设置为string类型用,原生的数据(泛型中的方法要转换为json)
/// </summary>
/// <param name="key">key</param>
/// <param name="value">值(字符串)</param>
/// <param name="expiresIn">过期时长</param>
/// <returns></returns>
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;】
源码:
// <summary>
/// Called within a lock
/// </summary>
/// <returns></returns>
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