池技術之common-pool2

  • 是什麼

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

優化對象的建立,和設計模式中的享元模式思路同樣git

  • 類解析

  • PooledObject 池化後的對象github

  • ObjectPool 對象池,redis

  • PooledObjectFactory 池對象工廠數據庫

  • GenericObjectPoolapache

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


  • PooledObjectFactory設計模式

根據本身的業務建立和管理要對象池化的對象
makeObject 建立對象
destroyObject 銷燬對象緩存

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

validateObject 檢測一個對象是否有效,無效會被銷燬
activateObject 激活一個對象或者說啓動對象的某些操做socket

  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源碼分析

做者:鉅子聯盟 連接:https://www.jianshu.com/p/f403f1782d1c 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。

相關文章
相關標籤/搜索