redis使用中經常出現 Could not get a resource from the pool 異常,解決辦法總結

背景:

最近使用jedis(redis)開發一項功能,查閱日誌發現,服務運行一段時間之後,就會出現

redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool 的異常,

重啓就好了,但是過一段時間又出現這種情況;

我這裏問題比較簡單,釋放連接的方法忘記調用了,應該是沒有及時釋放連接造成的連接池內無連接可用。


但是關於這個問題,發現網上有種種說法,主要是連接池配置釋放連接,關於這個問題,我們不妨深入思考下:

 

一、連接池配置參數怎麼設置?這些參數跟什麼有關係?

         1、最大連接數:MAX_ACTIVE,指的是支持同時在使用的最大的連接數量:

              參數的設置並不是越大越好,而是要看自己的實際使用情況,也就是需要的併發數是多少?

              比如,系統併發量一般100,但是你的設置是50,顯然這個時候加大設置是很有用的。

              但是,又比如這個業務最大可能併發100個鏈接,那麼就沒必要設置成2000,設置太大也可能會浪費系統性能;

              如果這時候設置遠遠高於實際了,還是出現連接不夠用的情況,那麼基本上就是你使用後忘記釋放了。

         2、等待可用連接的最大時間:MAX_WAIT,這個時間設置,要結合最大連接數、實際併發數、還有系統性能一起考慮,一般不宜過大,否則會導致請求響應時間過長,甚至請求失敗。但是也不能過小,如果系統資源比較富裕沒什麼影響,但是相反的話,過小的情況也可能造成較多的請求失敗

               比如,設置成1分鐘,那麼假設此時併發數較高(MAX_ACTIVE一直佔用着),一直沒有得到可用連接,那麼這個請求就會出現一直等待的情況,可能會出現給客戶的感覺上系統反應卡頓的不良體驗。

       3、有時爲了保證請求快速得到響應,保持一定的空閒連接(setMinIdle)。在連接池飽和狀態,最多有(MAX_ACTIVE-MinIdle)個連接數;

       4、如果併發數很大呢?大到超過redis支持的最大連接數?

           在 Redis2.4 中,最大連接數是被直接硬編碼在代碼裏面的,而在2.6版本中這個值變成可配置的。maxclients 的默認值是 10000.也就是說,redis默認最多允許10000個連接。當然這還要看硬件環境,CPU/內存情況;

           真的有再大的需求,就只能是分佈式/集羣了,這裏暫時不討論。

          附上jedis工具類中的獲取連接方法:

    private static String ADDR = "****";
    private static int PORT = 6379;
    
    //訪問密碼
    private static String AUTH = "****";
    
    //可用連接實例的最大數目,默認值爲8;
    //如果賦值爲-1,則表示不限制;如果pool已經分配了maxActive個jedis實例,則此時pool的狀態爲exhausted(耗盡)。
    private static int MAX_ACTIVE = 100;
    
    //控制一個pool最多有多少個狀態爲idle(空閒的)的jedis實例,默認值也是8。
    private static int MAX_IDLE = 10;
    
    //等待可用連接的最大時間,單位毫秒,默認值爲-1,表示永不超時。如果超過等待時間,則直接拋出JedisConnectionException;
    private static int MAX_WAIT = 1000;
    
    private static int TIMEOUT = 10000;
    
    //在borrow一個jedis實例時,是否提前進行validate操作;如果爲true,則得到的jedis實例均是可用的;
    private static boolean TEST_ON_BORROW = true;
    
    private static JedisPool jedisPool = null; 

//獲取鏈接
    public static synchronized Jedis getJedis(){
        if(jedisPool==null){
        	log.info("jedisPool==null , to reGet ");
        	try {
                JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
                //指定連接池中最大空閒連接數
                jedisPoolConfig.setMaxIdle(MAX_IDLE);
                //鏈接池中創建的最大連接數
                jedisPoolConfig.setMaxTotal(MAX_ACTIVE);
                //設置創建鏈接的超時時間
                jedisPoolConfig.setMaxWaitMillis(MAX_WAIT);
                //表示連接池在創建鏈接的時候會先測試一下鏈接是否可用,這樣可以保證連接池中的鏈接都可用的。
                jedisPoolConfig.setTestOnBorrow(true);
                jedisPool = new JedisPool(jedisPoolConfig,ADDR, PORT, TIMEOUT, AUTH);
                
			} catch (Exception e) {
				log.error("jedisPool get error "+e.toString());
			}
        }
        return jedisPool.getResource();
    }

二、釋放連接的替代方法

          在網上的資料,很多都是已經廢棄不用的方法,我這把替代方法貼一下:

          starting from Jedis 3.0 this method will not be exposed. Resource cleanup should be done using @see {@link redis.clients.jedis.Jedis#close()} (廢棄方法returnResourcereturnBrokenResource的註釋)

   //回收鏈接 
    public static synchronized void returnResource(Jedis jedis){
    	try {
    		if(jedis!=null){
    			/*
    			 * 這些方法已經廢棄
    			 */
        		jedisPool.returnResource(jedis);
//        		jedisPool.returnBrokenResource(jedis);
        		log.info("returnResource success ");
        	}
		} catch (Exception e) {
			log.error("returnResource error "+e.toString());
		}
    }
    
    //釋放鏈接
    public static synchronized void returnToPool(Jedis jedis){
    	try {
    		if(jedis!=null){
    			jedis.close();//替代方法
        		log.info("returnToPool success ");
        	}
		} catch (Exception e) {
			log.error("returnToPool error "+e.toString());
		}
    }

 

 

這次就總結到這兒,下次需要更深入研究下redis分佈式緩存。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章