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