commom-pool对象池

一、简介

comoms-pool2是Apache下一个开源的公共资源池。我们可以根据它来快速的建立一个自己的对象池。组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现。

大家常用的dbcp数据库连接池,也是基于comoms-pool实现。

 对象池化的基本思路是:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度上减少频繁创建对象所造成的开销。用于充当保存对象的”容器”,被称为对象池(Object Pool)

 

二、使用对象池的利与弊

1、

  • 增加复杂度:正常对象的生命周期由使用者和JVM维护:使用者创建(JVM分配内存)→ 销毁→ GC。对象池对象的生命周期更为复杂,需要引入多个状态:IDLE(空闲)、ALLOCATED(分配)、EVICTION(驱逐)、VALIDATION(验证)、INVALID(无效)、RETURING(返回中)。
  • 多线程资源竞争:commons-pool内部大量使用了synchronized及其他juc原子类,可能会造成锁竞争,影响程序性能
  • 参数配置失误可能会带来严重问题

2、

  • 复用池中对象,减少 创建对象、回收对象时所产生的开销

 

3、适用场景

  • 创建成本高昂的对象:并非所有对象都适合拿来池化,因为维护对象池也要造成一定开销。对生成时开销不大的对象进行池化,反而可能会出现“维护对象池的开销”大于“生成新对象的开销”,从而使性能降低的情况。但是对于生成时开销可观的对象,池化技术就是提高性能的有效策略了
  • 数量受限的资源:比如数据库连接
  • 资源受限的环境:cpu/内存等物理资源有限,垃圾收集,内存抖动会造成较大影响,需要提供内存管理效率,响应性比吞吐量更为重要

 

 

三、commons-pool结构分析

1、基本框架

它主要由3个大的部分组成:

ObjectPool:对象池,专门实现对象存取和状态管理的池实现。

PooledObject:池化对象,是对需要放到池里对象的一个包装类。添加了一些附加的信息,比如说状态信息,创建时间,激活时间,关闭时间等。这些添加的信息方便pool来管理和实现一些特定的操作。

PooledObjectFactory:对象工厂类,负责具体对象的创建、初始化、对象状态的销毁和验证,commons-pools框架本身提供了默认的抽象实现BasePooledObjectFactory,业务方在使用的时候只需要继承该类,然后实现wrap和create方法即可。

以上三者构成了一个object pool的基本框架。他们的关系可以用如下的一个图来描述:

 

 用一句话来概括他们整体的关系就是factory创建需要放入pool的对象,经过PooledObject包装一下就可以上架了

 

2、对象池Pool的创建

以GenericObjectPool为例

public GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) : 此方法创建一个GenericObjectPool实例

 GenericObjectPool类已经实现了和对象池有关的所有核心操作,开发者可以通过继承或者封装的方式来使用它,通过此构造函数,我们能够清晰的看到,一个Pool中需要指定PoolableObjectFactory实例,以及此对象池的Config信息。

此构造函数实例化了一个LinkedList作为”对象池”容器,用来存取“对象”。此外还会根据timeBetweenEvictionRunsMillis的值来决定是否启动一个后台线程,此线程用来周期性扫描pool中的对象列表,检测“对象池中的对象”空闲(idle)的时间是否达到了阈值,如果是则移除此对象

 

3、对象工厂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。
  • void activateObject(Object obj) “激活”对象,当Pool决定输出一个对象给调用者时,会调用此方法激活对象。
  • void void passivateObject(Object obj) : “钝化”对象,当调用者“归还对象”时,Pool将会钝化对象

 

 

4、ObjectPool接口

ObjectPool是一个顶层接口,里面定义了对象池的一些基本操作,GenericObjectPool是一个具体的对象池的实现。

复制代码
public interface ObjectPool<T> extends Closeable {

    /** 向对象池中添加对象 */
    void addObject() throws Exception, IllegalStateException,
            UnsupportedOperationException;

    /** 从对象池中借出对象 */
    T borrowObject() throws Exception, NoSuchElementException,
            IllegalStateException;

    /** 清空对象池,并销毁对象池中所有对象 */
    void clear() throws Exception, UnsupportedOperationException;

    /** 关闭对象池并释放资源 */
    @Override
    void close();

    /** 得到当前对象池已经借出的对象数量 */
    int getNumActive();

    /** 得到当前对象池中空闲对象数量 */
    int getNumIdle();

    /** 使对象无效*/
    void invalidateObject(T obj) throws Exception;
  
    /** 使对象归还到对象池中 */
    void returnObject(T obj) throws Exception;

}
复制代码

 

5、对象生命周期

 

 

四、config详解

  1. maxActive :连接池中最大连接数,默认为8
  2. maxIdle :连接池中最大空闲的连接数,默认为8
  3. minIdle :连接池中最少空闲连接数,默认为0
  4. maxWait:当连接池资源耗尽时,调用者最大阻塞的时间,超出将抛出异常。单位:毫秒数。默认为-1,表示永不超时
  5. minEvictableIdleTimeMillis :连接空闲的最小时间,达到此值后空闲连接将会被移除,负值(-1)表示不移除
  6. softMinEvictableIdleTimeMillis :连接空闲的最小时间,达到此值后空闲连接将会被移除,负值(-1)表示不移除
  7. numTestsPerEvictionRun : 对于“空闲连接”检测线程而言,每次检测的连接资源的个数。默认为3.
  8. testOnBorrow:向调用者输出”连接”对象时,是否进行有效性检测,如果无效则从连接池中移除,并尝试继续获取连接。默认为false
  9. testOnReturn :向连接池归还连接时。是否检测连接对象的有效性。默认false
  10. testWhileIdle :向调用者输出”连接”对象时,是否检测它的空闲超时,如果连接空闲超时,将会被移除,默认为false
  11. timeBetweenEvictionRunsMillis : “空闲连接”检测线程检测的周期,单位:毫秒数。如果为负值,表示不运行“检测线程”。默认为-1。
  12. whenExhaustedAction :当“连接池”中active数量达到阈值时,即“连接”资源耗尽时,连接池需要采取的手段: 0→抛出异常,1→阻塞,直到有可用连接资源(和maxWait参数有关,如果maxWait是正数,那么会等待maxWait的毫秒的时间,超时抛出异常,如果maxWait为负值,会永久等待),2→强制创建新的连接资源。默认为1
  13. lifo :pool可以被配置成LIFO队列(last -in-first-out)或FIFO(first-in-first-out)队列,来指定空闲对象被使用的次序,默认为true

 

 

五、示例

示例:sftp连接池

1、Maven依赖

 

复制代码
<dependencies>
    <dependency>
        <groupId>com.jcraft</groupId>
        <artifactId>jsch</artifactId>
        <version>0.1.55</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.8.0</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.14</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-freemarker</artifactId>
        <version>2.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.1.5.RELEASE</version>
    </dependency>
</dependencies>
复制代码

 

 

2、SftpClientObject(对象池内对象)

复制代码
package com.chenly.demo.pool;
import com.chenly.demo.config.SftpProperties;
import com.jcraft.jsch.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Properties;

/**
 * @author: chenly
 * @date: 2022-11-01 11:02
 * @description:
 * @version: 1.0
 */
@Component
@Data
@Slf4j
public class SftpClientObject {

    private ChannelSftp sftp;

    public SftpClientObject(SftpProperties sftpProperties) throws Exception{
        connect(sftpProperties);
    }
    /**
     * 连接登陆远程服务器
     */
    public ChannelSftp connect(SftpProperties sftpProperties) throws Exception  {
        JSch jSch = new JSch();
        Session session = null;
        session = jSch.getSession(sftpProperties.getUsername(), sftpProperties.getHost(), sftpProperties.getPort());
        session.setPassword(sftpProperties.getPassword());
        session.setConfig(this.getSshConfig());
        session.connect();
        sftp = (ChannelSftp)session.openChannel("sftp");
        sftp.connect();
        return sftp;
    }

    /**
     * 服务配置
     * @return
     */
    private Properties getSshConfig() {
        Properties sshConfig =  new Properties();
        sshConfig.put("StrictHostKeyChecking", "no");
        return sshConfig;
    }
    
    /**
     * 关闭连接
     */
    public void disconnect() {
        try {
            if(sftp!=null){
                if(sftp.getSession().isConnected()){
                    sftp.getSession().disconnect();
                }
                sftp.disconnect();
            }

        } catch (Exception e) {
            log.error("关闭与sftp服务器会话连接异常",e);
        }
    }

    //验证连接
    public boolean validateConnect() {
        try {
            if(sftp == null){
                return false;
            }
            return sftp.getSession().isConnected() && sftp.isConnected();
        } catch (Exception e) {
            return false;
        }
    }
}
复制代码

 

 

3SftpProperties (sftp连接配置、连接池配置)

 

复制代码
package com.chenly.demo.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * @author: chenly
 * @date: 2022-11-01 11:14
 * @description:
 * @version: 1.0
 */
@Component
@ConfigurationProperties("sftp")
@Data
public class SftpProperties {
    /** host */
    private String host;
    /**  端口号 */
    private int port = 22;
    /**  用户名*/
    private String username;
    /** 密码 */
    private String password;
    /** 字符集*/
    private String charset;
    /** sftp目录*/
    private String remotePath;
    /**  连接池配置 */
    private Pool pool;

    /**  连接池配置类 */
    @Data
    public static class Pool {
        /** 池中最小的连接数,只有当 timeBetweenEvictionRuns 为正时才有效*/
        private int minIdle = 0;
        /** 池中最大的空闲连接数,为负值时表示无限*/
        private int maxIdle = 8;
        /** 池可以产生的最大对象数,为负值时表示无限 */
        private int maxActive = 16;
        /** 当池耗尽时,阻塞的最长时间,为负值时无限等待 */
        private long maxWait = -1;
        /** 从池中取出对象是是否检测可用 */
        private boolean testOnBorrow = true;
        /**  将对象返还给池时检测是否可用*/
        private boolean testOnReturn = false;
        /** 检查连接池对象是否可用*/
        private boolean testWhileIdle = true;
        /** 距离上次空闲线程检测完成多久后再次执行*/
        private long timeBetweenEvictionRuns = 300000L;
    }
}
复制代码

 

 

4、SftpPooledObjectFactory (对象池内对象工厂类)

 

复制代码
package com.chenly.demo.pool;

import com.chenly.demo.config.SftpProperties;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
/**
 * @author: chenly
 * @date: 2022-11-01 11:41
 * @description:
 * @version: 1.0
 */
public class SftpPooledObjectFactory extends BasePooledObjectFactory<SftpClientObject> {

    private final SftpProperties sftpProperties;

    public SftpPooledObjectFactory(SftpProperties sftpProperties) {
        this.sftpProperties = sftpProperties;
    }

    @Override
    public SftpClientObject create() throws Exception {
        return new SftpClientObject(sftpProperties);
    }

    @Override
    public PooledObject<SftpClientObject> wrap(SftpClientObject sftpClient) {
        return new DefaultPooledObject<>(sftpClient);
    }

    @Override
    public boolean validateObject(PooledObject<SftpClientObject> p) {
        return p.getObject().validateConnect();
    }

    @Override
    public void destroyObject(PooledObject<SftpClientObject> p) {
        p.getObject().disconnect();
    }

}
复制代码

 

 

5、SftpObjectPool (对象池)

复制代码
package com.chenly.demo.pool;
import com.chenly.demo.config.SftpProperties;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

/**
 * @author: chenly
 * @date: 2022-11-01 11:17
 * @description:
 * @version: 1.0
 */
public class SftpObjectPool implements ObjectPool<SftpClientObject> {
    private  GenericObjectPool<SftpClientObject> internalPool;

    public SftpObjectPool(SftpProperties sftpProperties) {
        this.internalPool = new GenericObjectPool<SftpClientObject>(new SftpPooledObjectFactory(sftpProperties), getPoolConfig(sftpProperties.getPool())){
            @Override
            public void returnObject(SftpClientObject sftpClient) {
                try {
                    sftpClient.getSftp().cd(sftpProperties.getRemotePath());
                } catch (Exception e) {
                }
                super.returnObject(sftpClient);
            }
        };
    }

    @Override
    public void addObject() throws Exception {
        internalPool.addObject();
    }
    @Override
    public SftpClientObject borrowObject() throws Exception {
        return internalPool.borrowObject();
    }
    @Override
    public void clear() {
        internalPool.clear();
    }
    @Override
    public void close() {
        internalPool.close();
    }

    @Override
    public int getNumActive() {
        return internalPool.getNumActive();
    }
    @Override
    public int getNumIdle() {
        return internalPool.getNumIdle();
    }
    @Override
    public void invalidateObject(SftpClientObject obj) throws Exception {
        internalPool.invalidateObject(obj);
    }
    @Override
    public void returnObject(SftpClientObject obj) {
        internalPool.returnObject(obj);
    }
    private GenericObjectPoolConfig<SftpClientObject> getPoolConfig(SftpProperties.Pool properties) {
        if (properties == null) {
            properties = new SftpProperties.Pool();
        }
        GenericObjectPoolConfig<SftpClientObject> config = new GenericObjectPoolConfig<>();
        config.setMinIdle(properties.getMinIdle());
        config.setMaxIdle(properties.getMaxIdle());
        config.setMaxTotal(properties.getMaxActive());
        config.setMaxWaitMillis(properties.getMaxWait());
        config.setTestOnBorrow(properties.isTestOnBorrow());
        config.setTestOnReturn(properties.isTestOnReturn());
        config.setTestWhileIdle(properties.isTestWhileIdle());
        config.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRuns());
        return config;
    }
}
复制代码

 

6、SftpController (测试类)

 

复制代码
package com.chenly.demo.controller;

import com.chenly.demo.config.SftpProperties;
import com.chenly.demo.pool.SftpClientObject;
import com.chenly.demo.pool.SftpObjectPool;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

/**
 * @author: chenly
 * @date: 2022-11-01 11:18
 * @description:
 * @version: 1.0
 */
@Slf4j
@RestController
public class SftpController {

    @Resource
    private SftpObjectPool sftpPool;
    @Resource
    private SftpProperties sftpProperties;

    @GetMapping("/file/download")
    public Integer fileDownload() {
        SftpClientObject sftpClient = null;
        try {
            log.info("sftp连接池大小{}",sftpPool.getNumIdle());
            sftpClient = sftpPool.borrowObject();
            InputStream inputStream = sftpClient.getSftp().get("/test1/DMC_PROD_XXPT_TABLE_20221019.dat");
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            IOUtils.copy(inputStream,byteArrayOutputStream);
            return byteArrayOutputStream.size();
        } catch (Exception e1) {
            log.info("下载异常!",e1);
            try {
                sftpPool.invalidateObject(sftpClient);
            } catch (Exception e2) {
                log.info("销毁sftp连接异常!",e2);
            }
            sftpClient = null;
        } finally {
            if (null != sftpClient) {
                sftpPool.returnObject(sftpClient);
            }
        }
        return null;
    }
}
复制代码

 

 

7、配置文件application.properties

 

复制代码
server.port=9090
#sftp配置
sftp.host=192.168.2.32
sftp.port=22
sftp.username=sftp1
sftp.password=123qwe
sftp.charset=GBK
sftp.remote-path=/test1
sftp.pool.min-idle=0
sftp.pool.maxIdle=8
sftp.pool.maxActive=16
sftp.pool.maxWait=-1
sftp.pool.testOnBorrow=true
sftp.pool.testOnReturn=true
sftp.pool.timeBetweenEvictionRuns=300000
复制代码

 

 

参考地址:

common-pool对象池

posted @   harara  阅读(588)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示

目录导航