JedisPool使用Apache Common Pool里面的GenericObjectPool来管理连接对象,可以先看看以下文章来了解GenericObjectPool。
原文链接:https://www.cnblogs.com/chenjian5/p/5149312.html
某天,我在做压测的时候,发现Jedis偶尔会出现这个问题
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool at redis.clients.util.Pool.getResource(Pool.java:53) at redis.clients.jedis.JedisPool.getResource(JedisPool.java:226) ................... Caused by: java.net.SocketTimeoutException: connect timed out
先贴上配置信息
spring.redis.jedis.pool.max-active=1500 spring.redis.jedis.pool.max-wait=-1 spring.redis.jedis.pool.max-idle=50 spring.redis.jedis.pool.min-idle=10 spring.redis.timeout=3000
我来分析一下,
第一,这只是偶尔出现的问题,redis不可访问这个缘由先排除在外。
然后看看最大连接数,已经是1500了,按理来说已经是挺大的了,先不改。
接着,去检查代码里是否存在使用jedis后没有关闭连接的情况,没找到。
那是不是配置的参数有问题呢?
我在网上查找JedisPool配置参数的时候,看到一句话:maxIdle该参数一般尽量与maxActive一样,以提高并发数。
真的是这样吗,我们来看一下源码。我在org.apache.commons.pool2.impl.GenericObjectPool#returnObject看到这个代码:
int maxIdleSave = this.getMaxIdle(); if (this.isClosed() || maxIdleSave > -1 && maxIdleSave <= this.idleObjects.size()) { try { this.destroy(p); } catch (Exception var11) { this.swallowException(var11); } }
代码里显示,当空闲对象数量大于maxIdle,归还的对象就直接被销毁了。
会不会有一种情况,在高并发之后,大量对象销毁,连接关闭。然后在销毁功能还没结束的时候,又来了一波高并发,导致新建的连接和正在销毁的连接超过了最大连接数,从而获取不到新的连接。
我来做个实验,修改一个配置信息,提高maxIdle看看
spring.redis.jedis.pool.max-idle=500
接着对程序进行多次压测,很惊喜没有出现上面说的异常了。
在压测结束后,我们赶紧来看一下JedisPool的信息
从图中可以看出,活跃对象加上空闲对象等于500个。由于我们设置了max-idle=500,所以在没有请求的时候,空闲对象只有500个。当有请求过来时,就从这500个空闲对象当中去取Jedis对象。
我们再来看看JedisPoolConfig默认的配置
public class JedisPoolConfig extends GenericObjectPoolConfig { public JedisPoolConfig() { this.setTestWhileIdle(true); this.setMinEvictableIdleTimeMillis(60000L); this.setTimeBetweenEvictionRunsMillis(30000L); this.setNumTestsPerEvictionRun(-1); } }
由于minEvictableIdleTimeMillis默认是60秒,一分钟之后我们再来看看JedisPool信息
从图中可以看出,活跃对象加上空闲对象等于10个。由于我们设置了min-idle=10。
最后,再将maxIdle修改成1500,再做几次实验。图就不贴上来了。
这个实验中,我提高了maxIdle,从而被销毁的连接减少,没有出现上面所说的异常,但是还是有可能会出现异常的。我觉得maxIdle参数与maxActive一样,就能更好避免出现这个异常。而且,好像没必要把maxIdle与maxActive设置成不一样吧。