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。