工作笔记-记一次Jedis连接泄露的问题及解决过程

问题追踪

我使用的Jedis版本是2.9.0,通过连接池访问,在做压力测试时,发现在高并发环境下,连接泄露,连接池内的连接未能正常返还,导致"could not get resource fron pool". 

看使用代码:

	public void execute(RedisAction callback) {
		Jedis jedis = jedisPool.getResource();

		if (jedis == null) {
			throw new RuntimeException("fetch jedis connection failed ");
		}
		try {
			callback.doInRedis(jedis);
		} finally {
			jedis.close();
		}
	}

使用规范的try-finally方式,使用完毕后归还jedis连接,但是连接仍然泄露,在平稳运行一段时间后应用越来越慢,最后无法获取连接。

我推测是在Jedis归还过程中,由于高并发而导致的连接池泄露,看源码:

  @Override
  public void close() {
    if (dataSource != null) {
      if (client.isBroken()) {
        this.dataSource.returnBrokenResource(this);
      } else {
        this.dataSource.returnResource(this);
      }
      this.dataSource = null;
    } else {
      super.close();
    }
  }

发现:在jedis.close()的过程中,先将jedis归还(this.dataSource.returnResource(this)),再将dataSource设置为null. 问题就在这里了!

在高并发情况下,jedis被归还后的一瞬间即可能被其他线程借去,此时,上一次的close还没有完全执行完,在此时再执行下面的this.dataSource = null 这句,会导致下一个borrow到jedis连接的线程再也无法将连接归还给连接池,而只能走到super.close()将连接关闭。

解决方式

1. 翻github看到官方已经记录并且在2.10.2版本修复了这个问题, 只要将jedis版本升级到2.10.2以上就可以解决这个问题了,附上2.10.2的jedis.close()源码:

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

先设置dataSource为null,再归还Jedis连接, 就不会再有连接泄露了。

2. 某些情况下,可能无法直接这么简单地做jedis版本替换。

我一直有一个疑惑,那就是spring boot 2.1.x其实默认依赖的都是jedis2.9.x的版本,为什么使用springboot的时候不会出现jedis连接泄露呢?

我尝试看了spring-data-redis的源码,并没有找到什么特别的处理,工作时间太紧张,没时间追究,只得自己动手fix,当使用2.9.x版本的jedis时,避免连接泄露,在返还连接的时候,这么写:

	@SuppressWarnings("deprecation")
	public void closeJedis(Jedis jedis) {
		if (null != jedis) {
			if (jedisPool != null) {
				jedis.setDataSource(null);
				if (jedis.getClient().isBroken()) {
					jedisPool.returnBrokenResource(jedis);
				} else {
					jedisPool.returnResource(jedis);
				}
			} else {
				jedis.getClient().close();
			}
		}
	}

经过测试可解决问题。

关于spring到底做了什么解决了2.9.x版本的这个问题,有谁知道可以告诉我?网上真的查不到,自己看也看不出。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值