ShardedJedis 2.9.3版本高并发下连接池没有回收 Could not get a resource from the pool

本文分析了 Jedis 2.9.3 版本中 ShardedJedisPool 在高并发环境下出现资源未被正确回收的问题,并介绍了如何通过升级到 3.5.1 版本来解决该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、问题描述

Jedis version: 2.9.3 (问题版本)
压测代码片段:

// 初始化
ShardedJedisPool createShardedPool = new ShardedJedisPool(this.jedisPoolConfig, this.jedisShardInfos);

// 遍历get方法
ShardedJedis jedis = null
try {
    jedis  = createShardedPool.getConnection();
    jedis.get(rediskey.getBytes());
} finally {
    if (jedis != null) {
        jedis.close();
    }
}

redis异常:

error.kind:
redis.clients.jedis.exceptions.JedisException
message:
Could not get a resource from the pool
stack:
redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:51)
at redis.clients.jedis.ShardedJedisPool.getResource(ShardedJedisPool.java:36)

二、原因分析:

1、通过压测发现连接 dataSource 存在null情况
2、通过jedis官网issue找到问题原因
https://github.com/redis/jedis/issues/1920

Jedis.java
@Override
public void close() {
    if (dataSource != null) {
        if (client.isBroken()) {
            this.dataSource.returnBrokenResource(this);
        } else {
            this.dataSource.
                    returnResource(this);
        }
        this.dataSource = null;  // <-- This line 
    } else {
        super.close();
    }
}

JedisSentinelPool.java
@Override
public Jedis getResource() {
    while (true) {
        Jedis jedis = super.getResource();
        jedis.setDataSource(this);  // <-- This line 

        // get a reference because it can change concurrently
        final HostAndPort master = currentHostMaster;
        final HostAndPort connection = new HostAndPort(jedis.getClient().getHost(), jedis.getClient()
                .getPort());

        if (master.equals(connection)) {
            // connected to the correct master
            return jedis;
        } else {
            returnBrokenResource(jedis);
        }
    }
}

In the case of concurrency
Thread A return an object but not run to “this.dataSource = null” yet
Thread B borrow an object and set dataSource to this;
And Then Thread A run this.dataSource = null;
Finally Thread A will never returnResource because dataSource is null
Jedis version:
2.10.0

Jedis version: 2.10.2 官方代码优化:

Jedis.java
public void close() {
    if (this.dataSource != null) {
        Pool<Jedis> pool = this.dataSource;
        this.dataSource = null;
        if (this.client.isBroken()) {
            pool.returnBrokenResource(this);
        } else {
            pool.returnResource(this);
        }
    } else {
        super.close();
    }

}

但是 redis.clients.jedis.ShardedJedis#close 没有优化:

@Override
public void close() {
  if (dataSource != null) {
    boolean broken = false;

    for (Jedis jedis : getAllShards()) {
      if (jedis.getClient().isBroken()) {
        broken = true;
        break;
      }
    }

    if (broken) {
      dataSource.returnBrokenResource(this);
    } else {
      dataSource.returnResource(this);
    }
    this.dataSource = null;

  } else {
    disconnect();
  }
}

最新的3.5.1 redis.clients.jedis.ShardedJedis#close 已经优化:

@Override
  public void close() {
    if (dataSource != null) {
      boolean broken = false;

      for (Jedis jedis : getAllShards()) {
        if (jedis.isBroken()) {
          broken = true;
          break;
        }
      }
      ShardedJedisPool pool = this.dataSource;
      this.dataSource = null;
      if (broken) {
        pool.returnBrokenResource(this);
      } else {
        pool.returnResource(this);
      }

    } else {
      disconnect();
    }
  }

三、 解决方案:

结果发现升级到最新的2.X => 2.10.2 本不能解决ShardedJedis 只能解决Jedis连接回收问题,最终升级到3.5.1 解决了ShardedJedis 高并发情况下没有回收连接池问题。

四、心得总结:

如果怀疑是redis sdk问题,应该第一时间去看看开源的issue,而不是自己去压测debug 浪费了很多时间;自己发现了没有回收的问题也是一度怀疑是这几行代码的问题,就是没有充分证据说明是sdk bug。最后感谢开源的力量。
https://github.com/redis/jedis/issues/1920

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值