apache-common pool的使用

jopen 10年前發布 | 168K 次閱讀 Java開發 apache-common

Apache commons-pool本質上是"對象池",即通過一定的規則來維護對象集合的容器;commos-pool在很多場景中,用來實現"連接池"/"任務worker池"等,大家常用的dbcp數據庫連接池,也是基于commons-pool實現.

    commons-pool實現思想非常簡單,它主要的作用就是將"對象集合"池化,任何通過pool進行對象存取的操作,都會嚴格按照"pool配置"(比如池的大小)實時的創建對象/阻塞控制/銷毀對象等.它在一定程度上,實現了對象集合的管理以及對象的分發.

    1) 將創建對象的方式,使用工廠模式;

    2) 通過"pool配置"來約束對象存取的時機

    3) 將對象列表保存在隊列中(LinkedList)

 

    首選需要聲明,不同的"對象池"(或者連接池)在設計上可能存在很大的區別,但是在思想上大同小異,本文主要講解commons-pool,它和其他"連接池"的區別在此不多討論.

 

一.對象生命周期


apache-common pool的使用
 

 

二.Config詳解:

  1. maxActive: 鏈接池中最大連接數,默認為8.
  2. maxIdle: 鏈接池中最大空閑的連接數,默認為8.
  3. minIdle: 連接池中最少空閑的連接數,默認為0.
  4. maxWait: 當連接池資源耗盡時,調用者最大阻塞的時間,超時將跑出異常。單位,毫秒數;默認為-1.表示永不超時.
  5. minEvictableIdleTimeMillis: 連接空閑的最小時間,達到此值后空閑連接將可能會被移除。負值(-1)表示不移除。
  6. softMinEvictableIdleTimeMillis: 連接空閑的最小時間,達到此值后空閑鏈接將會被移除,且保留“minIdle”個空閑連接數。默認為-1.
  7. numTestsPerEvictionRun: 對于“空閑鏈接”檢測線程而言,每次檢測的鏈接資源的個數。默認為3.
  8. testOnBorrow: 向調用者輸出“鏈接”資源時,是否檢測是有有效,如果無效則從連接池中移除,并嘗試獲取繼續獲取。默認為false。建議保持默認值.
  9. testOnReturn:  向連接池“歸還”鏈接時,是否檢測“鏈接”對象的有效性。默認為false。建議保持默認值.
  10. testWhileIdle:  向調用者輸出“鏈接”對象時,是否檢測它的空閑超時;默認為false。如果“鏈接”空閑超時,將會被移除。建議保持默認值.
  11. timeBetweenEvictionRunsMillis:  “空閑鏈接”檢測線程,檢測的周期,毫秒數。如果為負值,表示不運行“檢測線程”。默認為-1.
  12.  whenExhaustedAction: 當“連接池”中active數量達到閥值時,即“鏈接”資源耗盡時,連接池需要采取的手段, 默認為1:
     -> 0 : 拋出異常,
     -> 1 : 阻塞,直到有可用鏈接資源
     -> 2 : 強制創建新的鏈接資源

    這些屬性均可以在org.apache.commons.pool.impl.GenericObjectPool.Config中進行設定。

 

三.原理解析

    1) 對象池創建(參考GenericObjectPool):

  • public GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) : 此方法創建一個GenericObjectPool實例,GenericObjectPool類已經實現了和對象池有關的所有核心操作,開發者可以通過繼承或者封裝的方式來使用它.通過此構造函數,我們能夠清晰的看到,一個Pool中需要指定PoolableObjectFactory 實例,以及此對象池的Config信息.PoolableObjectFactory主要用來"創建新對象",比如當對象池中的對象不足時,可以使用 PoolableObjectFactory.makeObject()方法來創建對象,并交付給Pool管理.

    此構造函數實例化了一個LinkedList作為"對象池"容器,用來存取"對象".此外還會根據timeBetweenEvictionRunsMillis的值來決定是否啟動一個后臺線程,此線程用來周期性掃描pool中的對象列表,已檢測"對象池中的對象"空閑(idle)的時間是否達到了閥值,如果是,則移除此對象.

Java代碼  
  1. if ((getMinEvictableIdleTimeMillis() > 0) &&  
  2.         (idleTimeMilis > getMinEvictableIdleTimeMillis())) {  
  3.     removeObject = true;  
  4. }  
  5. ...  
  6. if (removeObject) {  
  7.     try {  
  8.         _factory.destroyObject(pair.value);  
  9.     } catch(Exception e) {  
  10.         // ignored  
  11.     }  
  12. }  

 

 

    2) 對象工廠PoolableObjectFactory接口:

    commons-pool通過使用ObjectFactory(工廠模式)的方式將"對象池中的對象"的創建/檢測/銷毀等特性解耦出來,這是一個非常良好的設計思想.此接口有一個抽象類BasePoolableObjectFactory,可供開發者繼承和實現.

  • Object makeObject() : 創建一個新對象;當對象池中的對象個數不足時,將會使用此方法來"輸出"一個新的"對象",并交付給對象池管理.
  • void destroyObject(Object obj) : 銷毀對象,如果對象池中檢測到某個"對象"idle的時間超時,或者操作者向對象池"歸還對象"時檢測到"對象"已經無效,那么此時將會導致"對象銷毀";"銷毀對象"的操作設計相差甚遠,但是必須明確:當調用此方法時,"對象"的生命周期必須結束.如果object是線程,那么此時線程必須退出;如果object是socket操作,那么此時socket必須關閉;如果object是文件流操作,那么此時"數據flush"且正常關閉.
  • boolean validateObject(Object obj) : 檢測對象是否"有效";Pool中不能保存無效的"對象",因此"后臺檢測線程"會周期性的檢測Pool中"對象"的有效性,如果對象無效則會導致此對象從Pool中移除,并destroy;此外在調用者從Pool獲取一個"對象"時,也會檢測"對象"的有效性,確保不能講"無效"的對象輸出給調用者;當調用者使用完畢將"對象歸還"到Pool時,仍然會檢測對象的有效性.所謂有效性,就是此"對象"的狀態是否符合預期,是否可以對調用者直接使用;如果對象是Socket,那么它的有效性就是socket的通道是否暢通/阻塞是否超時等.
  • void activateObject(Object obj) : "激活"對象,當Pool中決定移除一個對象交付給調用者時額外的"激活"操作,比如可以在activateObject方法中"重置"參數列表讓調用者使用時感覺像一個"新創建"的對象一樣;如果object是一個線程,可以在"激活"操作中重置"線程中斷標記",或者讓線程從阻塞中喚醒等;如果 object是一個socket,那么可以在"激活操作"中刷新通道,或者對socket進行鏈接重建(假如socket意外關閉)等.
  • void void passivateObject(Object obj) : "鈍化"對象,當調用者"歸還對象"時,Pool將會"鈍化對象";鈍化的言外之意,就是此"對象"暫且需要"休息"一下.如果object是一個 socket,那么可以passivateObject中清除buffer,將socket阻塞;如果object是一個線程,可以在"鈍化"操作中將線程sleep或者將線程中的某個對象wait.需要注意的時,activateObject和passivateObject兩個方法需要對應,避免死鎖或者"對象"狀態的混亂.

    3) ObjectPool接口與實現:

    對象池是commons-pool的核心接口,用來維護"對象列表"的存取;其中GenericObjectPool是其實現類,它已經實現了相關的功能.

  • Object borrowObject() : 從Pool獲取一個對象,此操作將導致一個"對象"從Pool移除(脫離Pool管理),調用者可以在獲得"對象"引用后即可使用,且需要在使用結束后"歸還".如下為偽代碼:
Java代碼  
  1. public Object borrowObject() throws Exception {  
  2.     Object value = null;  
  3.     synchronized (this) {  
  4.         if(!_pool.isEmpty()){  
  5.             value = _pool.remove();  
  6.         }  
  7.           
  8.     }         
  9.     for(;;) {       
  10.         //如果Pool中沒有"對象",則根據相應的"耗盡"策略  
  11.         if(value == null) {  
  12.             switch(whenExhaustedAction) {  
  13.                 //如果耗盡,仍繼續創建新"對象"  
  14.                 case WHEN_EXHAUSTED_GROW:  
  15.                     value = _factory.makeObject();  
  16.                     break;  
  17.                 //如果耗盡,則終止,此時以異常的方式退出.  
  18.                 case WHEN_EXHAUSTED_FAIL:  
  19.                     throw new NoSuchElementException("Pool exhausted");  
  20.                 //如果耗盡,則阻塞,直到有"對象"歸還  
  21.                 case WHEN_EXHAUSTED_BLOCK:  
  22.                     try {  
  23.                         synchronized (value) {  
  24.                             if (value == null) {  
  25.                                 //maxWait為Config中指定的"最大等待時間"  
  26.                                 if(maxWait <= 0) {  
  27.                                     latch.wait();  
  28.                                 } else {  
  29.                                         latch.wait(waitTime);  
  30.                                 }  
  31.                             } else {  
  32.                                 break;  
  33.                             }  
  34.                         }  
  35.                     } catch(InterruptedException e) {         
  36.                         //  
  37.                         break;  
  38.                     }  
  39.                       
  40.                 default://  
  41.             }  
  42.         }  
  43.   
  44.         try {  
  45.             _factory.activateObject(latch.getPair().value);  
  46.             if(_testOnBorrow &&  
  47.                     !_factory.validateObject(latch.getPair().value)) {  
  48.                 throw new Exception("ValidateObject failed");  
  49.             }  
  50.            return value;  
  51.         }  
  52.         catch (Throwable e) {  
  53.             try {  
  54.                 _factory.destroyObject(latch.getPair().value);  
  55.             } catch (Throwable e2) {  
  56.                 //  
  57.             }  
  58.         }  
  59.     }  
  60. }  

 

  • void returnObject(Object obj) : "歸還"對象,當"對象"使用結束后,需要歸還到Pool中,才能維持Pool中對象的數量可控,如果不歸還到Pool,那么將意味著在Pool之外,將有大量的"對象"存在,那么就使用了"對象池"的意義.如下為偽代碼:
Java代碼  
  1. public void returnObject(Object obj) throws Exception {  
  2.     try {  
  3.         boolean success = true;//  
  4.         if(_testOnReturn && !(_factory.validateObject(obj))) {  
  5.             success = false;  
  6.         } else {  
  7.             _factory.passivateObject(obj);  
  8.         }  
  9.   
  10.         synchronized (this) {  
  11.             //檢測pool中已經空閑的對象個數是否達到閥值.  
  12.             if((_maxIdle >= 0) && (_pool.size() >= _maxIdle)) {  
  13.                 success = false;  
  14.             } else if(success) {  
  15.                 _pool.addFirst(new ObjectTimestampPair(obj));  
  16.             }  
  17.         }  
  18.   
  19.         // Destroy the instance if necessary  
  20.         if(!success) {  
  21.             try {  
  22.                 _factory.destroyObject(obj);  
  23.             } catch(Exception e) {  
  24.                 // ignored  
  25.             }  
  26.         }  
  27.     } catch (Exception e) {  
  28.         //  
  29.     }  
  30. }  
  • void invalidateObject(Object obj) : 銷毀對象,直接調用ObjectFactory.destroyObject(obj);.
  • void addObject() : 開發者可以直接調用addObject方法用于直接創建一個"對象"并添加到Pool中.

四.代碼實例.

    本實例主要用來演示一個"TCP連接池".

    1) ConnectionPoolFactory.java:

Java代碼  
  1. import org.apache.commons.pool.BasePoolableObjectFactory;  
  2. import org.apache.commons.pool.impl.GenericObjectPool;  
  3. import org.apache.commons.pool.impl.GenericObjectPool.Config;  
  4.   
  5. public class ConnectionPoolFactory {  
  6.   
  7.     private GenericObjectPool pool;  
  8.   
  9.     public ConnectionPoolFactory(Config config,String ip,int port){  
  10.         ConnectionFactory factory = new ConnectionFactory(ip, port);  
  11.         pool = new GenericObjectPool(factory, config);  
  12.     }  
  13.       
  14.     public Socket getConnection() throws Exception{  
  15.         return (Socket)pool.borrowObject();  
  16.     }  
  17.       
  18.     public void releaseConnection(Socket socket){  
  19.         try{  
  20.             pool.returnObject(socket);  
  21.         }catch(Exception e){  
  22.             if(socket != null){  
  23.                 try{  
  24.                     socket.close();  
  25.                 }catch(Exception ex){  
  26.                     //  
  27.                 }  
  28.             }  
  29.         }  
  30.     }  
  31.       
  32.     /** 
  33.      * inner  
  34.      * @author qing 
  35.      * 
  36.      */  
  37.     class ConnectionFactory extends BasePoolableObjectFactory {  
  38.   
  39.         private InetSocketAddress address;  
  40.           
  41.         public ConnectionFactory(String ip,int port){  
  42.             address = new InetSocketAddress(ip, port);  
  43.         }  
  44.           
  45.         @Override  
  46.         public Object makeObject() throws Exception {  
  47.             Socket socket = new Socket();  
  48.             socket.connect(address);  
  49.             return socket;  
  50.         }  
  51.           
  52.         public void destroyObject(Object obj) throws Exception  {  
  53.             if(obj instanceof Socket){  
  54.                 ((Socket)obj).close();  
  55.             }  
  56.         }  
  57.   
  58.         public boolean validateObject(Object obj) {  
  59.             if(obj instanceof Socket){  
  60.                 Socket socket = ((Socket)obj);  
  61.                 if(!socket.isConnected()){  
  62.                     return false;  
  63.                 }  
  64.                 if(socket.isClosed()){  
  65.                     return false;  
  66.                 }  
  67.                 return true;  
  68.             }  
  69.             return false;  
  70.         }  
  71.   
  72.   
  73.     }  
  74.       
  75. }  

 

    2) TestMain.java(測試類):

Java代碼  
  1. public class TestMain {  
  2.   
  3.     /** 
  4.      * @param args 
  5.      */  
  6.     public static void main(String[] args) {  
  7.         Config config = new Config();  
  8.         config.maxActive = 16;  
  9.         config.maxWait = 30000;  
  10.         ConnectionPoolFactory poolFactory = new ConnectionPoolFactory(config, "127.0.0.1"8011);  
  11.         Socket socket = null ;  
  12.         try{  
  13.             socket = poolFactory.getConnection();  
  14.             ////  
  15.         }catch(Exception e){  
  16.             e.printStackTrace();  
  17.         }finally{  
  18.             if(socket != null){  
  19.                 poolFactory.releaseConnection(socket);  
  20.             }  
  21.         }  
  22.   
  23.     }  
  24.   
  25. }  
 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!