在項目中使用redis做緩衝,當運行一段時間後就會出現如下錯誤:Could not get a resource from the pool,然後在看具體的異常資訊就是JedisPool中擷取不到jedis對象,也就是說串連池中沒有可用的jedis。
自己的第一反應就是把最大連結數(setMaxTotal)調大一些,剛開始設定了100、後來200、在後來2000都不行
然後上網一搜發現大家的回答也都是修改最大串連數,如下demo就是網上一篇部落格的解釋:
1、產生原因:用戶端去redis伺服器拿串連(代碼描述的是租用對象borrowObject)的時候,池中無可用串連,即池中所有串連被佔用,且在等待時候設定的逾時時間後還沒拿到時,報出此異常。
2、解決辦法:調整JedisPoolConfig中maxActive為適合自己系統的閥值。
<bean id="dataJedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> [color=red]<property name="maxActive" value="5000"/>[/color] <property name="maxIdle" value="5000"/> <property name="maxWait" value="10000"/> <property name="testOnBorrow" value="true"/></bean>
但這個自己也設定了,配置如下:
#最大使用中的物件數 redis.pool.maxTotal=1000 #最大能夠保持idel狀態的對象數 redis.pool.maxIdle=100 #最小能夠保持idel狀態的對象數 redis.pool.minIdle=50 #當池內沒有返回對象時,最大等待時間 redis.pool.maxWaitMillis=10000 #當調用borrow Object方法時,是否進行有效性檢查 redis.pool.testOnBorrow=true #當調用return Object方法時,是否進行有效性檢查 redis.pool.testOnReturn=true #“空閑連結”檢測線程,檢測的周期,毫秒數。如果為負值,表示不運行“檢測線程”。預設為-1. redis.pool.timeBetweenEvictionRunsMillis=30000 #向調用者輸出“連結”對象時,是否檢測它的空閑逾時; redis.pool.testWhileIdle=true # 對於“空閑連結”檢測線程而言,每次檢測的連結資源的個數。預設為3. redis.pool.numTestsPerEvictionRun=50 #表示一個對象至少停留在idle狀態的最短時間,然後才能被idle object evitor掃描並驅逐;這一項只有在timeBetweenEvictionRunsMillis大於0時才有意義MinEvictableIdleTimeMillis=60000#redis伺服器的IP redis.ip=xxxxxx #redis伺服器的Port redis1.port=6379
但是這樣設定後,當運行一段時候後還是會報同樣的錯誤,說句不愛聽的話最大連結數“1000”這個值真的不小,但還是錯誤,所以肯定不是這個值的原因。
後來在網上找到了一篇文章,文章中的jedis工具類中有三個方法,代碼如下:
public class JedisUtils {private static Log logger = LogFactory.getLog(JedisUtils.class);/** * 自動注入Redis串連執行個體對象線程池 */@Autowiredprivate JedisPool jedisPool;/** * 擷取Jedis對象 * * @return */public synchronized Jedis getJedis() {Jedis jedis = null;if (jedisPool != null) {try {if (jedis == null) {jedis = jedisPool.getResource();}} catch (Exception e) {logger.error(e.getMessage(), e);}}return jedis;}/** * 回收Jedis對象資源 * * @param jedis */public synchronized void returnResource(Jedis jedis) {if (jedis != null) {jedisPool.returnResource(jedis);}}/** * Jedis對象出異常的時候,回收Jedis對象資源 * * @param jedis */public synchronized void returnBrokenResource(Jedis jedis) {if (jedis != null) {jedisPool.returnBrokenResource(jedis);}}}
有兩個回收方法發現沒,一個是正常結束時調用釋放jedis資源而另一個是在出現異常時調用釋放jedis資源。
業務類、方法代碼如下:
Jedis jedis = jedisUtils.getJedis(); if(jedis == null){ throw new NullPointerException("Jedis is Null"); } try{ long queueCurrValue = jedis.llen(messageQueueName); if(queueCurrValue >= queueMaxSize){ return addFlag; } String serizlizetValue = JSON.toJSONString(message); if(StringUtils.isNotBlank(serizlizetValue)){ long queueLength = jedis.lpush(messageQueueName, serizlizetValue); if(queueLength - queueCurrValue > 0){ addFlag = true; } } }catch(Exception e){ jedisUtils.returnBrokenResource(jedis); logger.error(e.getMessage(), e); }finally{ jedisUtils.returnResource(jedis); }
看到沒在finally{} 代碼塊和 catch(){} 異常中都調用了相關方法,來釋放jedis資源,這樣就不會出現之前的那種異常了,當然最大連結數也不用設定那麼大,下面看看修改後的設定檔
#最大連結數MaxTotal=100#空閑時最大連結數MaxIdle=20#空閑最小MinIdle=8#連結最大等待時間 (毫秒)MaxWaitMillis=10000
就這值,再也沒出現擷取不到資源的的異常,問題解決了,所以問題不是最大連結數小了,而是沒有釋放資源,所以不管你設定多大的值都會出現異常而且消耗了大量的資源。
單我在項目中使用的時候有如下問題:
// 釋放資源public synchronized void returnResource(Jedis jedis) {if (jedis != null) {pool.returnResource(jedis);}}// 出現異常釋放資源public synchronized void returnBrokenResource(Jedis jedis) {if (jedis != null) {pool.returnBrokenResource(jedis);}}
兩個方法 returnResource(jedis)、 和 returnBrokenResource(jedis) 都被畫了橫線即過時廢棄了,而還不知替換方法所以有知道的請指教,先謝謝了。
相關文章推薦:
http://www.codeweblog.com/redis%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%90%AD%E5%BB%BA-%E9%85%8D%E7%BD%AE-%E5%8F%8Ajedis%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95/
http://www.codeweblog.com/jedis%E8%BF%9E%E6%8E%A5%E6%B1%A0%E9%85%8D%E7%BD%AE/
http://www.codeweblog.com/jedis-returnresource%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/
http://fengguang0051.iteye.com/blog/2237171
不過其中的兩篇文章也會比自己轉載做筆記的……