JedisPool 在关闭情况下为什么 还能提供连接资源 并且 只有一个线程一直拿不到连接资源
问题描述
昨日在产品发版的时候,项目的启动之后,有个线程(下面称之为线程A
)一直在抛jedis异常,异常信息如下
线程A
会随着程序启动一直运行之后,每10s会去缓存中取一次数据,报错就是在取数据的时候发生的。
以下是jedisDao
类,get(key)
的代码
@Autowire
private JedisPool jedisPool;
@Override
public String get(String key) {
Jedis jedis = null;
String string;
try {
jedis = jedisPool.getResource();
string = jedis.get(key);
} finally {
if (jedis != null) {
jedis.close();
}
}
return string;
}
神奇的是,整个业务没有受影响,其他的线程都能够从这个jedisPool
中获取连接资源。只有线程A
一直在抛这个异常。
jedisPoolConfig
<!-- 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30"/>
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10"/>
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024"/>
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000"/>
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000"/>
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000"/>
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500"/>
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="true"/>
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true"/>
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false"/>
</bean>
临时解决方案
调大了最大连接数,重启了程序,这个报错就不见了。
<!-- 最大连接数 -->
<property name="maxTotal" value="50"/>
初步探索和疑问
我先是就 Pool not open
的报错google了一下,github jedis的issue下面有相关的提问
Could not get a resource from the pool #1159
意思就是说,在取资源的时候,jedisPool 处于被关闭的状态。看了下相关的源码,首先是jedisPool.getResource()
@Override
public Jedis getResource() {
Jedis jedis = super.getResource();
jedis.setDataSource(this);
return jedis;
}
// super.getResource();
public T getResource() {
try {
return internalPool.borrowObject();
} catch (NoSuchElementException nse) {
throw new JedisException("Could not get a resource from the pool", nse);
} catch (Exception e) {
throw new JedisConnectionException("Could not get a resource from the pool", e);
}
}
// 定义在GenericObjectPool中的borrowObject()
public T borrowObject(long borrowMaxWaitMillis) throws Exception {
// 在这里报了错
assertOpen();
AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
(getNumIdle() < 2) &&
(getNumActive() > getMaxTotal() - 3) ) {
removeAbandoned(ac);
}
...
}
// GenericObjectPool#assertOpen()
final void assertOpen() throws IllegalStateException {
if (isClosed()) {
throw new IllegalStateException("Pool not open");
}
}
public final boolean isClosed() {
return closed;
}
如issue里说的,jedisPool实际上是去内部成员internalPool中borrowObject - 获取连接资源,在我的案例中,线程A
获取连接资源的时候,这个内部连接池的内部变量closed = true
,导致抛出了如上图的异常。
那么就有几个疑问了
- internalPool这个连接池对象是在什么情况下被close掉的呢?我看了下代码,只有在调用jedisPool的
destroy()
或者close()
方法的时候,内部变量closed
才会被设为true。但是我检查了项目代码,没有地方显式调用了destroy()
或者close()
方法。难道是jedis自己内部的某块逻辑做了这个操作?请问各位大神是否有了解呢?
- 既然jedisPool目前的状态是closed,为什么其他操作缓存的操作是正常的呢?都是会调用s上面提到的
jedisDao
类的get(key)
。 - 由于是生产环境上出现的,我暂时没有如何重现的这个bug的思路,可否麻烦各路大神给我提供下本地重现的思路?
- 重启之后就没有这个问题了,这又是为什么呢?是因为调大了连接数,然后
线程A
启动的时候就拿到了连接所以就没事情了?那也说不通啊,其他操作缓存的线程最后都是会调用jedis.close
的方法的,连接资源应该会释放才对。为什么之前一直拿不到?
小弟我对jedis的了解暂浅,希望能借此机会能和社区各位前辈请教下经验,希望能得到你们宝贵的意见。
如果有什么我需要补充的信息,也请告诉我。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
请问这个问题有没有解决,目前我也遇到!!