前言
新系统构建时需要分布式Session&Cache支撑,为子系统之间提供服务和数据共享。测试环境为单机搭建三主三从的集群,一次测试突然报错Connection Rest (链接重置了)。后续排查发现是Redis吃了3500多个链接!发现redis链接不释放并且一直在Ping!
链接不释放
问题分析
很明显就是没有回收链接!并且一直在ping导致jedis认为链接是有效无法释放。
解决思路
1. 调整JedisPool大小
因为之前压测链接不够配置成600现调整为120的最大链接(没看懂为什么我设置的600 redis链接可以到3500多,先解决链接不释放问题)
2. 控制最大空闲链接
之前设置是150个现设置成50个
3.打开空闲链接释放检查
设置空闲链接检查时间为5S检查一次
结果是链接还是不释放并且链接总数340多!
代码如下:
@Bean("jedisPoolConfig")
public JedisPoolConfig jedisPoolConfig(){
JedisPoolConfig jpc = new JedisPoolConfig() ;
jpc.setMaxIdle(50);
jpc.setMinIdle(20);
jpc.setMaxTotal(120);
jpc.setNumTestsPerEvictionRun(50);
jpc.setTestOnBorrow(true);
jpc.setTestOnReturn(false);
jpc.setBlockWhenExhausted(true);
jpc.setTimeBetweenEvictionRunsMillis(5000);
jpc.setTestWhileIdle(true);
jpc.setMaxWaitMillis(15000);
return jpc ;
}
@Bean("jedisConnectionFactory")
public JedisConnectionFactory jcf(@Qualifier("jedisPoolConfig") JedisPoolConfig jpc){
RedisClusterConfiguration rcc = new RedisClusterConfiguration(Arrays.asList("redis链接信息"));
rcc.setMaxRedirects(6);
rcc.setPassword(RedisPassword.of("?????"));
return new JedisConnectionFactory(rcc,jpc) ;
}
到这来感觉有点无从下手了:驱逐政策如下
1. 设置了最大空闲链接 : pc.setMaxIdle(50);
2. 设置了清理空闲链接的配置 :jpc.setTimeBetweenEvictionRunsMillis(5000); jpc.setTestWhileIdle(true);
由上可知链接的创建&驱逐&使用全部是由Pool进行控制的。
于是乎我开始怀疑Jedis是不是检查空闲的代码有问题打开源代码如下:
如图可知 需要链接是通的并且可Ping通那Pool认为这个链接就是有效的!这逻辑看上去并没有问题!因此可知驱逐策略肯定受Ping和链接超时影响!由此可知驱逐策略就是在检查链接是否通讯正常&&ping(能解释一开始为什么大量链接在Ping)能否成功。使用驱逐策略必须破坏某个条件才可以触发。
新思路
1. redis服务器屏蔽Ping ---不推荐影响正常的存活检查策略
2. 想办法设置TCP链接时长 --- 这个看一下redis官网配置文件详解肯定有相关配置
查询Redis配置文件示例可知以下配置可达上述效果
timeout | 等待命令时间单位秒 |
tcp-keepalive | TCP保持。。。 |
|
打开redis配置文件发现timeout是默认值0永不超时!由此可知是因为链接没有超时时间,导致jedis客户端每一个TimeBetweenEvictionRunsMillis周期内的Ping命令生效。使JedisPool驱逐政策无法触发。所有我们只需要配置的驱逐时间稍大于redis服务器超时时间既可。到此链接不释放问题已解决。
redis链接数超过JedisPool最大链接数
问题分析
JedisPool代码应该不用怀疑,这些框架自带池不会出这种很明显的“BUG”。开始看源码!
通过链接不释放的问题可知链接是由JedisConnectionFactory 进行维护。开始分析JedisConnectionFactory源码。
源码分析
有继承Sping的生命周期钩子:
Bena对象初始化以后创建RedisCluster
初始化redis的KeySlot(创建Key和redis实例的映射:说明我这个Key路由到哪一个Redis上面)
找到Pool相关配置
发现核心创建Pool的代码
结论
综上所述我们配置的JedisPoolConfig并不是针对整个RedisCluster共享的池配置,而是针对每一个redis实例进行的描述。所以我发现maxConnect=120实际redis使用链接是340多是在正常范围内的!(ps: 340/6=56.66....是接近我们配置maxidle的而且稳定在这个范围)。