JedisPool中的Jedis對象個數是有限的,默認是8個。這裏假設使用的默認配置,若是有8個Jedis對象被佔用,而且沒有歸還,若是調用者還要從JedisPool中借用Jedis,就須要進行等待(例如設置了maxWaitMillis>0),若是在maxWaitMillis時間內仍然沒法獲取到Jedis對象就會拋出以下異常。java
1 2 3 4 |
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool … Caused by: java.util.NoSuchElementException: Timeout waiting for idle object at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449) |
還有一種狀況,就是設置了blockWhenExhausted=false,那麼調用者發現池子中沒有資源時,會當即拋出異常不進行等待,下面的異常就是blockWhenExhausted=false時的效果。git
1 2 3 4 |
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool … Caused by: java.util.NoSuchElementException: Pool exhausted at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:464) |
對於這個問題,須要重點討論的是爲何鏈接池沒有資源了,形成沒有資源的可能的緣由很是多github
1.客戶端:高併發下鏈接池設置太小,出現供不該求,因此會出現上面的錯誤,可是正常狀況下只要比默認的最大鏈接數(8個)多一些便可,由於正常狀況下JedisPool以及Jedis的處理效率足夠高。redis
2.客戶端:沒有正確使用鏈接池,好比沒有進行釋放,例以下面代碼所示:
定義JedisPool,使用默認的鏈接池配置。apache
1 2 3 4 5 6 7 8 9 10 11 12 |
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379); //向JedisPool借用8次鏈接,可是沒有執行歸還操做。 for (int i = 0; i < 8; i++) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.ping(); } catch (Exception e) { e.printStackTrace(); } } |
當調用者再向鏈接池借用Jedis時(以下操做),就會拋出異常:網絡
1 |
jedisPool.getResource().ping(); |
Jedis在調用Redis時,若是出現了讀寫超時後,會出現下面的異常:併發
1 |
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out |
形成該異常的緣由也有如下幾種:運維
Jedis在調用Redis時,若是出現了讀寫超時後,會出現下面的異常:tcp
1 |
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out |
形成該異常的緣由也有如下幾種:分佈式
Jedis在調用Redis時,若是出現客戶端數據流異常,會出現下面的異常。
1 |
redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream. |
形成這個異常緣由可能有以下幾種:
1 |
config set client-output-buffer-limit "normal 1048576 1048576 60 slave 268435456 67108864 60 pubsub 33554432 8388608 60" |
若是使用get命令獲取一個bigkey(例如3M),就會出現這個異常。
若是Redis當前正在執行Lua腳本,而且超過了lua-time-limit,此時Jedis調用Redis時,會收到下面的異常。對於如何處理這類問題(Lua lua-time-limit配置以前章節已經介紹了)
1 |
redis.clients.jedis.exceptions.JedisDataException: BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. |
Jedis調用Redis時,若是Redis正在加載持久化文件,那麼會收到下面的異常。
1 |
redis.clients.jedis.exceptions.JedisDataException: LOADING Redis is loading the dataset in memory |
Jedis調用Redis執行寫操做時,若是Redis的使用內存大於maxmemory的設置,會收到下面的異常,此時應該調整maxmemory並找到形成內存增加的緣由(maxmemory以前章節已經介紹了)
1 |
redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'. |
若是客戶端鏈接數超過了maxclients,新申請的鏈接就會出現以下異常:
1 |
redis.clients.jedis.exceptions.JedisDataException: ERR max number of clients reached |
此時新的客戶端鏈接執行任何命令,返回結果都是以下:
1 2 |
127.0.0.1:6379> get hello (error) ERR max number of clients reached |
這個問題可能會比較棘手,由於此時沒法執行Redis命令,通常來講能夠從兩個方面進行着手。
1.客戶端:若是maxclients參數不是很小的話,應用方的客戶端鏈接數基本不會超過maxclients,一般來看是因爲應用方對於Redis客戶端使用不當形成的。此時若是應用方是分佈式結構的話,能夠經過下線部分應用節點(例如佔用鏈接較多的節點),使得Redis的鏈接數先降下來。從而讓絕大部分節點能夠正常運行,此時在再經過查找程序bug或者調整maxclients進行問題的修復。
2.服務端:若是此時客戶端沒法處理,而當前Redis爲高可用模式(例如Redis Sentinel和Redis Cluster),能夠考慮將當前Redis作故障轉移。
此問題不存在肯定的解決方式,可是不管從哪一個方面進行處理,故障的快速恢復極爲重要,固然更爲重要的是找到問題的所在,不然一段時間後客戶端鏈接數依然會超過maxclients。
附贈GenericObjectPoolConfig的重要屬性
序號 | 參數名 | 含義 | 默認值 |
---|---|---|---|
1 | maxActive | 鏈接池中最大鏈接數 | 8 |
2 | maxIdle | 鏈接池中最大空閒的鏈接數 | 8 |
3 | minIdle | 鏈接池中最少空閒的鏈接數 | 0 |
4 | maxWaitMillis | 當鏈接池資源用盡後,調用者的最大等待時間(單位爲毫秒),通常不建議使用默認值 | -1:表示永遠不超時,一直等。 |
5 | jmxEnabled | 是否開啓jmx監控,若是應用開啓了jmx端口而且jmxEnabled設置爲true,就能夠經過jconsole或者jvisualvm看到關於鏈接池的相關統計,有助於瞭解鏈接池的使用狀況,而且能夠針對其作監控統計 | true |
6 | minEvictableIdleTimeMillis | 鏈接的最小空閒時間,達到此值後空閒鏈接將被移除 | 30分鐘 |
7 | numTestsPerEvictionRun | 作空閒鏈接檢測時,每次的採樣數 | 3 |
8 | testOnBorrow | 向鏈接池借用鏈接時是否作鏈接有效性檢測(ping),無效鏈接會被移除,每次借用多執行一次ping命令 | false |
9 | testOnReturn | 向鏈接池歸還鏈接時是否作鏈接有效性檢測(ping),無效鏈接會被移除,每次歸還多執行一次ping命令 | false |
10 | testWhileIdle | 向鏈接池借用鏈接時是否作鏈接空閒檢測,空閒超時的鏈接會被移除 | false |
11 | timeBetweenEvictionRunsMillis | 空閒鏈接的檢測週期(單位爲毫秒) | -1:表示不作檢測 |
12 | blockWhenExhausted | 當鏈接池用盡後,調用者是否要等待,這個參數是和maxWaitMillis對應的,只有當此參數爲true時,maxWaitMillis纔會生效 | true |
本文部份內容來自《Redis開發與運維》一書,轉載請聲明。