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,导致抛出了如上图的异常。

那么就有几个疑问了

  1. internalPool这个连接池对象是在什么情况下被close掉的呢?我看了下代码,只有在调用jedisPool的destroy()或者close()方法的时候,内部变量closed 才会被设为true。但是我检查了项目代码,没有地方显式调用了destroy()或者close()方法。难道是jedis自己内部的某块逻辑做了这个操作?

    请问各位大神是否有了解呢?

  2. 既然jedisPool目前的状态是closed,为什么其他操作缓存的操作是正常的呢?都是会调用s上面提到的jedisDao类的get(key)
  3. 由于是生产环境上出现的,我暂时没有如何重现的这个bug的思路,可否麻烦各路大神给我提供下本地重现的思路?
  4. 重启之后就没有这个问题了,这又是为什么呢?是因为调大了连接数,然后线程A启动的时候就拿到了连接所以就没事情了?那也说不通啊,其他操作缓存的线程最后都是会调用jedis.close的方法的,连接资源应该会释放才对。为什么之前一直拿不到?

小弟我对jedis的了解暂浅,希望能借此机会能和社区各位前辈请教下经验,希望能得到你们宝贵的意见。

如果有什么我需要补充的信息,也请告诉我。

以上是 JedisPool 在关闭情况下为什么 还能提供连接资源 并且 只有一个线程一直拿不到连接资源 的全部内容, 来源链接: utcz.com/p/181287.html

回到顶部