redis连接释放问题记录
redis连接释放问题记录
记录一次在压测后发现的redisTemplate使用场景下,redis的连接资源没有释放的问题。
@
问题描述
springboot 版本:2.1.2(排除了lettuce的依赖)
jedis版本:2.9.1
场景:高并发情况下,RedisTemplate获取连接失败并阻塞线程导致TPS下降。
异常描述:如果设置了max-wait则在等待时间到后抛出异常:Timeout waiting for idle object。如果没有设置,则线程将被一直阻塞。
问题追踪
public T borrowObject(long borrowMaxWaitMillis) throws Exception {
//.....
p = (PooledObject)this.idleObjects.takeFirst();
} else {
p = (PooledObject)this.idleObjects.pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS);
}
}
if (p == null) {
throw new NoSuchElementException("Timeout waiting for idle object");
}
//.......
}
阻塞点在this.idleObjects.takeFirst()方法,如果设置了等待时间,则调用pollFirst()。debug可以发现,资源没有释放。
这个可以看当前对象中的borrowedCount和returnedCount发现,borrowedCount大于returnedCount。
borrowedCount:借用的资源计数
returnedCount:归还的资源计数
问题定位
borrowObject(long borrowMaxWaitMillis):获取资源的方法
returnObject(T obj):归还资源的方法
本来以为是归还资源的方法有bug导致的资源没有归还,但是经过多次debug分析后,发现问题点实际在于jedis的close方法上。
public void close() {
if (this.dataSource != null) {
if (this.client.isBroken()) {
this.dataSource.returnBrokenResource(this);
} else {
this.dataSource.returnResource(this);
}
//问题点在于这里
this.dataSource = null;
} else {
super.close();
}
}
场景描述:
在多线程环境下,通过debug可以看到,当前jedis对象是共享的。
假如有线程1,2.
当线程1获取的资源的持有,线程2等待资源释放场景。
然后线程1在执行close时,通过returnResource释放了资源,而线程2拿到了资源。
然后线程2也执行到了close方法,而线程1执行了this.dataSource = null;
这时线程2则不会归还资源,直接执行了close,导致出现的连接没有释放问题。
问题解决
解决方式:
- 升级jedis版本或者回退jedis版本。
- 更换连接,可以考虑使用lettuce。