Apache Torque的關鍵配置參數 (DBCP)

起因

頻繁的connection close/open.

DBA點名:有個component從3個禮拜前突然開始頻率的打開/關閉db的connection,大大抵消了使用connection pool的好處



調查

經探索發現,該component使用Torque來進行db connection pool的管理。而Torque又使用DBCP來管理connection pool。

而DBCP實際上又使用Object Pooling來。


在DBCP 的configuration中有這樣的提示

 If maxIdle is set too low on heavily loaded systems it is possible you will see connections being closed and almost immediately new connections being opened. This is a result of the active threads momentarily closing connections faster than they are opening them, causing the number of idle connections to rise above maxIdle. The best value for maxIdle for heavily loaded system will vary but the default is a good starting point.


這個現象和我們遇到的問題完全一致。然後重新check下產品上Torque的配置:

torque.dsfactory.XXX.pool.maxIdle=6
torque.dsfactory.XXX.pool.maxActive=30


毫無疑問,maxIdle配置有誤。DBCP的默認配置maxIdle和maxActive大小相同。

轉到GenericKeyedObjectPool的javadoc


DBCP Param
paramDescritpionUsage
maxActive最大活動對象數目active是指被client正在使用的對象;可被認爲是對象(資源)數目的峯值
maxIdle最大空閒對象數目      對象返回到Pool時,Pool的最大size;可被認爲是對象(資源)數目的平均值          
timeBetweenEvictionRunsMillis回收任務的運行間隔默認是每5分鐘檢查一個Pool是否有對象Idle > minEvictableIdleTimeMillis
minEvictableIdleTimeMillis空閒對象的存活時間默認如果該對象1小時內沒有被使用,則認爲可以被回收,比如真正的釋放數據庫連接
minIdle最小空閒對象數目對象返回到Pool時/Pool初始化時,Pool的最小size


Common Pool 1.3 源碼解析

borrowObject //僅抽取其主要分支,另外忽略key的存在,看作僅僅有單個pool
long starttime = System.currentTimeMillis();
boolean newlyCreated = false;
for(;;)
{
   LinkedList pool = (LinkedList)(_poolMap.get(key)); //Pool爲鏈接結構
   pair = (ObjectTimestampPair)(pool.removeFirst()); //取pool中隊頭元素
   if(null == pair) 
   {
     int active = getActiveCount(key); //當前active數目,被client正在使用的對象數目
     if ((_maxActive < 0 || active < _maxActive) //小於最大active,創建新的對象 
     {
         Object obj = _factory.makeObject(key);
         pair = new ObjectTimestampPair(obj);
         newlyCreated = true;
     }
     else
     {
         switch(_whenExhaustedAction) //根據exhaust策略來創建,拋異常,或等待
         {
              case WHEN_EXHAUSTED_GROW:
                           Object obj = _factory.makeObject(key);
                           pair = new ObjectTimestampPair(obj);
                           break;
              case WHEN_EXHAUSTED_FAIL:
                          throw new NoSuchElementException();
              case WHEN_EXHAUSTED_BLOCK:
                          if(_maxWait <= 0) 
                          {
                               wait();  //不限時等待
                          }
                          else
                          {
                             final long elapsed = (System.currentTimeMillis() - starttime);
                             final long waitTime = _maxWait - elapsed;
                             if (waitTime > 0)
                             {
                               wait(waitTime); //限時等待
                             }
                          }
                         //被喚醒或者限時已到
                         if(_maxWait > 0 && ((System.currentTimeMillis() - starttime) >= _maxWait)) 
                         {
                               throw new NoSuchElementException("Timeout waiting for idle object");//timeout異常
                         }
                         else 
                         {
                                 continue; // 重新嘗試獲取對象
                         }                
         }
     }

     incrementActiveCount(key); //active count++
   }
}

returnObject
LinkedList pool = (LinkedList) (_poolMap.get(key));
decrementActiveCount(key); // active count--
if(_maxIdle >= 0 && (pool.size() >= _maxIdle))  //pool中已有足夠閒置對象
{
   shouldDestroy = true; //直接銷燬
}
else
{
   pool.addLast(new ObjectTimestampPair(obj)); //放入隊尾,更新對象訪問時間戳
}

notifyAll(); //喚醒borrowObject中等待的線程

if(shouldDestroy)
{
  _factory.destroyObject(key, obj); //銷燬對象
}

看完上面兩個方法,真相已經大白。
  • common pool採用鏈接隊列(LinkedList)存放Idle 對象,其pool命名爲idlePool更爲合適;borrowObject從隊列移除對象,returnObject(或者初始化)放對象到隊列中。
  • 隊列採用先前先出模式(隊頭取,隊尾放),更老的對象先取出。
  • 採用acitve count進行計數,統計client正在使用的對象數目;Idle不需要另外計數, pool size即爲idle數目
所以maxIdle一旦設置小於系統的資源負載,common pool會頻繁的創建/銷燬對象。比如maxIdle設置爲6,而系統負載爲14。
  • 假定初始爲空,borrowObject創建14個對象;
  • 一半的資源(7)釋放被同時釋放,多餘的1個對象會被銷燬。
  • borrow 8個對象;pool中有6個idle,2個新的對象被創建。
  • 9個對象同時被釋放,這時多餘的3個對象被銷燬。
  • 。。。
在系統實際運行中,只要系統的平均負載大於maxIdle,而且client使用釋放資源都很短暫,則極有可能出現對象不斷創建和銷燬的情況。

清除任務,每次運行默認檢查3個對象
evict
final LinkedList list = (LinkedList)_poolMap.get(key);
if (_evictLastIndex < 0 || _evictLastIndex > list.size()) {
      _evictLastIndex = list.size(); //不同的pool共用
}
objIter = list.listIterator(_evictLastIndex); // 從隊尾開始  

ObjectTimestampPair pair = (ObjectTimestampPair)(objIter.previous()); //使用list iterator
boolean removeObject=false
if(_minEvictableIdleTimeMillis > 0 &&
    System.currentTimeMillis() - pair.tstamp > _minEvictableIdleTimeMillis) //閒置過長
{
     removeObject=true;
}



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