JedisPool无法获得资源问题
線上碰到一個(gè)問題:
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:22)
線上會(huì)相隔不定時(shí)的天數(shù)后出現(xiàn)一次JedisPool種getresouce拿不到resource的情況。中間陸陸續(xù)續(xù)上過很多次線,然后廢了很大勁努力排除掉了業(yè)務(wù)可能和多次上線的代碼問題。業(yè)務(wù)數(shù)據(jù)量即便是在測試環(huán)境種建造了更多,也不會(huì)導(dǎo)致那種情況的出現(xiàn)。而業(yè)務(wù)代碼測試環(huán)境和線上相同,后來在測試環(huán)境壓測的壓力和線上差不多的情況下,也不會(huì)重現(xiàn)這個(gè)問題。
后來就有一種束手無策的感覺了,最后只能推論是當(dāng)時(shí)應(yīng)用集群到Redis集群的網(wǎng)絡(luò)出了問題了,但是由于種種原因一直沒有在集群間添加網(wǎng)絡(luò)狀態(tài)的監(jiān)控,也就只能是猜測了,但是又沒辦法重現(xiàn)。后來偷偷在線上的一臺(tái)服務(wù)器上面添加了ping的監(jiān)控,很簡單:ping -i 1 192.168.134.155 > pinglog_{`date +%Y-%m-%d`}.log &,該命令的效果比較簡單,就是每隔1sping一次目標(biāo)服務(wù)器,然后打印到按天分開的日志里面。然而這種事情不再出現(xiàn)我們酒沒辦法驗(yàn)證推論,領(lǐng)導(dǎo)又催的非常緊,沒辦法還是需要驗(yàn)證出來啊。
開始的時(shí)候,根據(jù)代碼來找原因,代碼里面從jedispool種獲得jedis資源實(shí)例的代碼是使用了java7里面的try-with-resouce的寫法,也就是用完之后,于是就懷疑是不是這種寫法,在try塊里面有了其他異常會(huì)導(dǎo)致resouce無法正常關(guān)閉,導(dǎo)致某個(gè)Jedis實(shí)例用完后沒有還給JedisPool,導(dǎo)致資源不足?
?
public class JedisTest {private static final JedisPool jedisPool;static {JedisPoolConfig config = new JedisPoolConfig();config.setMaxIdle(20);config.setMaxTotal(40);config.setMinIdle(10);jedisPool = new JedisPool(config, "127.0.0.1", 8279, 1000);}public static void main(String[] args) {try(Jedis jedis = jedisPool.getResource()){throw new Exception("~");}catch (Exception e){//do nothing }} }后來其實(shí)在JedisPool里面的斷點(diǎn)很容易就可以看到j(luò)ava7 并沒有錯(cuò)誤,多心了。
于是,那還是回歸主題,其實(shí)只要認(rèn)真分析,不會(huì)又那么困難的問題出現(xiàn):
其實(shí)getresouce報(bào)錯(cuò)有兩種可能:
1、本身有錯(cuò)誤---排除,首先如果這個(gè)方法有錯(cuò)誤,那么之前應(yīng)該會(huì)一直出現(xiàn),或者其他人也早該把開源包的錯(cuò)誤爆出,排除這種可能;
2、就是在規(guī)定時(shí)間內(nèi)沒取到資源。
剛才我們看maxtotal里面定義了池子最大就40個(gè),如果真的40個(gè)都在用,并且在超時(shí)的100ms內(nèi)沒人return resouce,那報(bào)錯(cuò)也正常。
也就是說,我們出現(xiàn)了40個(gè)全部被用到,并且在超時(shí)的100ms內(nèi)沒有任何資源還給JedisPool。
后來恰好,在打印的jstack的信息種發(fā)現(xiàn)了大量的time_waiting狀態(tài)的線程在等待從Jedispool.getResouce().
那么什么情況下會(huì)導(dǎo)致這個(gè)情況出現(xiàn)?
假設(shè)現(xiàn)在并發(fā)來了41個(gè)請(qǐng)求,然后其中40個(gè)正常的進(jìn)行,但是第41沒拿到資源,于是等待規(guī)定的超時(shí)時(shí)間,但是這會(huì)從應(yīng)用到Redis集群間網(wǎng)絡(luò)出現(xiàn)抖動(dòng),暫時(shí)不通,會(huì)導(dǎo)致40個(gè)請(qǐng)求種的里面的jedis的get或者set操作變慢甚至超時(shí)。
我們?cè)O(shè)想一種情況:從jedisPool里面拿資源的超時(shí)時(shí)間是100ms,程序里面進(jìn)行g(shù)et或者set資源的是200ms超時(shí),那么就有可能出現(xiàn)這種情況。
事實(shí)證明我們的配置確實(shí)是jedis里面去get或者set一個(gè)key的時(shí)候,超時(shí)時(shí)間是200ms,那也就是說,如果網(wǎng)絡(luò)發(fā)生了抖動(dòng),那就會(huì)在并發(fā)的情況下迅速耗光資源池,然后超時(shí)后報(bào)錯(cuò)才還回去,但是那個(gè)時(shí)間早就發(fā)生了getResouce的錯(cuò)誤。
Bingo,其實(shí)很簡單的原因,那就是沒有正確的理解兩個(gè)超時(shí)時(shí)間之間的關(guān)系。
我們可以簡單測試一下:
由于本地環(huán)境問題,只是示例代碼,就不執(zhí)行了。其實(shí)很容易就還原了問題出來。
后面只要調(diào)小jedis的get和set方法的超時(shí)時(shí)間,同時(shí)也盡量小的使用getresource的超時(shí)時(shí)間(這里為什么不加大,因?yàn)樵诟卟l(fā)的情況下會(huì)迅速耗光線程數(shù)量,jstack里面甚至出現(xiàn)了500個(gè)線程有450個(gè)是time_waiting的狀態(tài),這可不是我們想要的結(jié)果)。
然后在服務(wù)器之間添加監(jiān)控和警報(bào),及時(shí)報(bào)警進(jìn)行網(wǎng)絡(luò)的修復(fù)。
轉(zhuǎn)載于:https://www.cnblogs.com/congsg2016/p/5328976.html
總結(jié)
以上是生活随笔為你收集整理的JedisPool无法获得资源问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: markdown--入门
- 下一篇: Crontab命令格式