池技術之common-pool2

  • 是什么

apache commons-pool是apache基金會的一個開源對象池組件,我們常用的數據庫連接池dpcp和redis的java客戶端jedis都使用commons-pool來管理連接

優化對象的創建,和設計模式中的享元模式思路一樣

  • 類解析

  • PooledObject 池化后的對象

  • ObjectPool 對象池,

  • PooledObjectFactory 池對象工廠

  • GenericObjectPool

實現了對對象池的管理,是一個基本的對象池實現
borrowObject 從對象池中獲取一個對象
returnObject 對象使用完之后,歸還到對象池


  • PooledObjectFactory

根據自己的業務創建和管理要對象池化的對象
makeObject 創建對象
destroyObject 銷毀對象

  1. 對象的空閑時間(idle)超時
  2. 使用完被檢測到對象已經無效時
    當調用這個方法之后,對象的生命周期必須結束。如果是對象是線程,線程必須已結束,如果是socket,socket必須已close,如果是文件操作,文件數據必須已flush,且文件正常關閉.

validateObject 檢測一個對象是否有效,無效會被銷毀
activateObject 激活一個對象或者說啟動對象的某些操作

  1. 檢測空閑對象的時候,且設置了測試空閑對象是否可以用,就會調用這個方法.
  2. borrowObject的時候
  3. 如果對象是一個包含參數的對象,可以在這里進行初始化

passivateObject 鈍化對象
在向對象池歸還一個對象是會調用這個方法。這里可以對對象做一些清理操作。比如清理掉過期的數據,下次獲得對象時,不受舊數據的影響

一般來說activateObject和passivateObject是成對出現的。前者是在對象從對象池取出時做一些操作,后者是在對象歸還到對象池做一些操作,可以根據自己的業務需要進行取舍。


  • 參數配置類GenericObjectPoolConfig

lifo: 對象池存儲空閑對象是使用的LinkedBlockingDeque,建議使用默認值true
fairness: 是否使用lock的公平鎖(不公平的性能高5-10倍,獲取鎖時沒排隊,沒有先到先得的概念)。默認值是false,建議使用默認值。
maxWaitMillis: 當沒有空閑連接時,獲取一個對象的最大等待時間。如果這個值小于0,則永不超時,一直等待,直到有空閑對象到來。如果大于0,則等待maxWaitMillis長時間,如果沒有空閑對象,將拋出NoSuchElementException異常。默認值是-1;可以根據需要自己調整,單位是毫秒。
minEvictableIdleTimeMillis: 當空閑的時間大于這個值時,執行移除這個對象操作,默認值30分鐘。這個參數是強制性的,只要空閑時間超過這個值,就會移除.小于0則為Long的最大值
softMinEvictableIdleTimeMillis: 對象最小的空閑時間 ,和minEvictableIdleTimeMillis邏輯一樣,區別是:它會保留最小的空閑對象數量。而上面的不會,是強制性移除的。默認值是-1;
numTestsPerEvictionRun: 檢測空閑對象線程每次檢測的空閑對象的數量。默認值是3;如果這個值小于0,則每次檢測的空閑對象數量等于當前空閑對象數量除以這個值的絕對值,并對結果向上取整

testOnCreate: 在創建對象時檢測對象是否有效,true是,默認值是false。
testOnBorrow: 在從對象池獲取對象時是否檢測對象有效,true是;默認值是false。
testOnReturn: 在向對象池中歸還對象時是否檢測對象有效,true是,默認值是false。
testWhileIdle: 在檢測空閑對象線程檢測到對象不需要移除時,是否檢測對象的有效性。true是,默認值是false。

timeBetweenEvictionRunsMillis:空閑對象檢測線程的執行周期,即多長時候執行一次空閑對象檢測。單位是毫秒數。如果小于等于0,則不執行檢測線程。默認值是-1;

blockWhenExhausted: 當對象池沒有空閑對象時,新的獲取對象的請求是否阻塞。true阻塞。默認值是true;

maxTotal:對象池中管理的最多對象個數。默認值是8。

maxIdle:對象池中最大的空閑對象個數。默認值是8。

minIdle:對象池中最小的空閑對象個數。默認值是0。

  • 怎么用

  1. 添加依賴
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.3</version>
</dependency>
  1. 需要池化的對象,緩存的對象
public class Resource {

    private static int id;
    private int rid;
    
    public Resource() {
        synchronized (this) {
            this.rid = id++;
        }
    }
    
    public int getRid() {
        return this.rid;
    }
    
    @Override
    public String toString() {
        return "id:" + this.rid;
    }
    
}
  1. 創建工廠類
public class MyPoolableObjectFactory extends BasePooledObjectFactory<Resource>{
    
    /**
     * 創建一個對象實例
     */
    @Override
    public Resource create() throws Exception {
        return new Resource();
    }
    
    /**
     * 包裹創建的對象實例,返回一個pooledobject
     */
    @Override
    public PooledObject<Resource> wrap(Resource obj) {
        return new DefaultPooledObject<Resource>(obj);
    }
    
}

  1. 客戶端調用
public class Test {

    public static void main(String[] args) {
        // 創建池對象工廠
        PooledObjectFactory<Resource> factory = new MyPoolableObjectFactory();
        
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        // 最大空閑數
        poolConfig.setMaxIdle(5);
        // 最小空閑數, 池中只有一個空閑對象的時候,池會在創建一個對象,并借出一個對象,從而保證池中最小空閑數為1
        poolConfig.setMinIdle(1);
        // 最大池對象總數
        poolConfig.setMaxTotal(20);
        // 逐出連接的最小空閑時間 默認1800000毫秒(30分鐘)
        poolConfig.setMinEvictableIdleTimeMillis(1800000);
        // 逐出掃描的時間間隔(毫秒) 如果為負數,則不運行逐出線程, 默認-1
        poolConfig.setTimeBetweenEvictionRunsMillis(1800000 * 2L);
        // 在獲取對象的時候檢查有效性, 默認false
        poolConfig.setTestOnBorrow(true);
        // 在歸還對象的時候檢查有效性, 默認false
        poolConfig.setTestOnReturn(false);
        // 在空閑時檢查有效性, 默認false
        poolConfig.setTestWhileIdle(false);
        // 最大等待時間, 默認的值為-1,表示無限等待。
        poolConfig.setMaxWaitMillis(5000);
        // 是否啟用后進先出, 默認true
        poolConfig.setLifo(true);
        // 連接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true
        poolConfig.setBlockWhenExhausted(true);
        // 每次逐出檢查時 逐出的最大數目 默認3
        poolConfig.setNumTestsPerEvictionRun(3);
        
        // 創建對象池
        final GenericObjectPool<Resource> pool = new GenericObjectPool<Resource>(factory, poolConfig);  
        
        for (int i = 0; i < 40; i++) {  
            new Thread(new Runnable() {  
                @Override  
                public void run() {  
                    try {  
                        Resource resource = pool.borrowObject();// 注意,如果對象池沒有空余的對象,那么這里會block,可以設置block的超時時間  
                        System.out.println(resource);  
                        Thread.sleep(1000);  
                        pool.returnObject(resource);// 申請的資源用完了記得歸還,不然其他人要申請時可能就沒有資源用了  
                    } catch (Exception e) {  
                        e.printStackTrace();  
                    }  
                }  
            }).start();  
        }  
    }
    
}

  • 例二:
  1. 創建對象
/**
 * FileName :Conn
 * Author :zengzhijun
 * Date : 2018/5/30 19:41
 * Description:
 */
package com.byedbl.pool.example1;


import org.slf4j.LoggerFactory;

/**
 * common-pool2 使用方式
 * <p/>
 * 假設這是一個建立TCP連接的對象,該對象的初始化時間平均為500ms,為了避免在程序中頻繁創建Conn對象,我們需要借助對象池管理Conn對象實例
 *
 * @author : zengzhijun
 * @date : 2018/5/30 19:42
 **/
public class Conn {
    /**
     * 記錄對象的創建時間
     */
    private long createTime;


    /**
     * 初始化Conn對象,模擬創建Conn對象平均消耗500ms
     * @throws InterruptedException
     */
    public Conn() throws InterruptedException {
        Thread.sleep(500);
        createTime = System.currentTimeMillis();
        LoggerFactory.getLogger(getClass()).debug(" init conn suc... " + createTime);
    }

    /**
     * 報告Conn對象信息
     */
    public void report() {
        LoggerFactory.getLogger(getClass()).info("this is a available conn " + createTime);
    }
}

  1. 創建對象的工廠
/**
 * FileName :ConnFactory
 * Author :zengzhijun
 * Date : 2018/5/30 19:46
 * Description:
 */
package com.byedbl.pool.example1;

import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;

/**
 * common-pool2 使用方式
 * <p/>
 * 為了使用common-pool2對象池管理,我們必須實現{@link org.apache.commons.pool2.PooledObjectFactory}或者其子類
 * 這是一個工廠模式,告訴對象池怎樣去創建要管理的對象
 * <p/>
 * BasePooledObjectFactory 是對{@link org.apache.commons.pool2.PooledObjectFactory}的一個基本實現,我們可以繼承該類,減少一些方法的實現
 * <p/>
 * 在實現{@link org.apache.commons.pool2.PooledObjectFactory}接口時,我們一定要實現的接口方法是{@link org.apache.commons.pool2.PooledObjectFactory#makeObject()}方法。
 *
 * @author : zengzhijun
 * @date : 2018/5/30 19:46
 **/
public class ConnFactory extends BasePooledObjectFactory<Conn> {


    /**
     * 間接實現{@link org.apache.commons.pool2.PooledObjectFactory#makeObject()}方法,表明怎樣創建需要管理對象
     */
    @Override
    public Conn create() throws Exception {
        return new Conn();
    }


    /**
     * 在common-pool2中為了統計管理的對象的一些信息,比如調用次數,空閑時間,上次使用時間等,需要對管理的對象進行包裝,然后在放入到對象池中
     *
     * @param obj 對象池要管理的對象
     * @return 返回包裝后的PooledObject對象
     */
    @Override
    public PooledObject<Conn> wrap(Conn obj) {
        return new DefaultPooledObject<Conn>(obj);
    }
}

  1. 創建對象池
/**
 * FileName :ConnPool
 * Author :zengzhijun
 * Date : 2018/5/30 19:51
 * Description:
 */
package com.byedbl.pool.example1;

import org.apache.commons.pool2.impl.GenericObjectPool;

/**
 * common-pool2 使用方式
 * <p/>
 * Conn對象管理池,這里利用 GenericObjectPool 作為對象池
 *
 * @author : zengzhijun
 * @date : 2018/5/30 19:52
 **/
public class ConnPool extends GenericObjectPool<Conn> {


    /**
     * 調用{@link GenericObjectPool}的構造方法,構造ConnPool
     */
    public ConnPool() {
        super(new ConnFactory(), new ConnPoolConfig());
    }

    /**
     * 調用{@link GenericObjectPool}的構造方法,構造ConnPool
     */
    public ConnPool(ConnPoolConfig connPoolConfig) {
        super(new ConnFactory(), connPoolConfig);
    }
}

  1. 對象池配置
/**
 * FileName :ConnPoolConfig
 * Author :zengzhijun
 * Date : 2018/5/30 19:50
 * Description:
 */
package com.byedbl.pool.example1;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

/**
 * common-pool2 使用方式
 * <p/>
 * {@link org.apache.commons.pool2.impl.GenericObjectPool}支持個性化配置,我們可以配置對象池中總共的對象數,最大、最小空閑對象數等等
 * 這邊繼承{@link GenericObjectPoolConfig}是為了ConnPool也可以進行個性化的配置
 *
 * @author : zengzhijun
 * @date : 2018/5/30 19:50
 **/
public class ConnPoolConfig  extends GenericObjectPoolConfig {

    public ConnPoolConfig() {
        // defaults to make your life with connection pool easier :)
        setMinIdle(5);
        setTestOnBorrow(true);
    }
}

  1. 客戶端用法
package com.byedbl.pool.example1;

import org.junit.Test;

public class ConnPoolTest {

    @Test
    public void conn() throws Exception {
        ConnPoolConfig connPoolConfig = new ConnPoolConfig();
        connPoolConfig.setMinIdle(5);
        connPoolConfig.setMaxIdle(8);
        ConnPool connPool = new ConnPool(connPoolConfig);
        Conn conn1 = connPool.borrowObject();
        Conn conn2 = connPool.borrowObject();
        Conn conn3 = connPool.borrowObject();
        Conn conn4 = connPool.borrowObject();
        Conn conn5 = connPool.borrowObject();
        conn1.report();
        connPool.returnObject(conn1);
        conn2.report();
        connPool.returnObject(conn2);
        conn3.report();
        connPool.returnObject(conn3);
        conn4.report();
        connPool.returnObject(conn4);
        conn5.report();
        connPool.returnObject(conn5);

        conn5.report();

        // 被歸還的對象的引用,不可以在次歸還
        // java.lang.IllegalStateException: Object has already been retured to this pool or is invalid
        try {
            connPool.returnObject(conn5);
        }catch (Exception e){
            e.printStackTrace();
        }
    }


}

common-pool2連接池詳解與使用
common-pool2 使用
使用示例源碼
一個FTP的工具工程
thrift-client-manager
apache-common-pool2源碼分析
commons-pool2源碼分析

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容