GenericKeyedObjectPool

​ 通用池化框架commons-pool2实践,其中提到了可以池化一个对象和一组对象,一个对象用到了GenericObjectPool这个类,一组对象用到了GenericKeyedObjectPool这个类。顾名思义,键值对象池。就是通过一个key对应一个对象类型来组合对象池,其本质上就是一个Mapkey是自定义,value就是org.apache.commons.pool2.ObjectPool,而但对象池化类GenericObjectPool也是实现了这个接口。

注意:

  1. Map用的ConcurrentHashMap,是线程安全的。
  2. 获取和回收太频繁,会遇到性能问题。

1. 依赖

<dependency>
     <groupId>org.apache.commons</groupId>
     <artifactId>commons-pool2</artifactId>
     <version>2.7.0</version>
</dependency>

2. 配置

例如:配置多sftp主机连接

sftp:
  hosts:
    remote-1:
      host: 127.0.0.1
      port: 22
      username: root
      password: 1234
    remote-2:
      host: 127.0.0.2
      port: 22
      username: root
      password: 1234

3. 连接对象类

  • 被对象池管理的对象
  • 可以想象为一个连接,创建复杂,比如数据库或者redis或者socket之类的对象
@Data
@ConfigurationProperties("sftp")
public class SftpProperties {
    private String host = "localhost";
    private int port = 22;
    private String username;
    private String password = "";
    private LinkedHashMap<String, SftpProperties> hosts;
  	...
}

4. 对象池工厂

然后就是池化工厂类,这个类需要定义keyvalue的类型,然后就是照葫芦画瓢。继承BaseKeyedPooledObjectFactory<K, V>类,并重写create()、wrap()等方法

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

/**
 * 对象池工厂
 */
private static class KeyedPooledClientFactory extends BaseKeyedPooledObjectFactory<String, SftpClient> {

    private final Map<String, SftpProperties> SftpPropertiesMap;

    private KeyedPooledClientFactory(Map<String, SftpProperties> SftpPropertiesMap) {
      this.SftpPropertiesMap = SftpPropertiesMap;
    }

    /**
 	* 根据当前key(remote-1、remote-2)得到的配置信息,创建sftp客户端
 	*/
    public SftpClient create(String key) {
      //SftpClient构造中,根据key拿到对应的主机配置后初始化sftp连接对象
      return new SftpClient(SftpPropertiesMap.get(key));
    }

  	/**
     * common-pool2 中创建了 DefaultPooledObject 对象对对象池中对象进行的包装。
     * 将我们自定义的对象放置到这个包装中,工具会统计对象的状态、创建时间、更新时间、返回时间、出借时间、使用时间等等信息进行统计
     */
    public PooledObject<SftpClient> wrap(SftpClient sftpClient) {
      return new DefaultPooledObject<>(sftpClient);
    }

    /**
     * 校验对象是否可用
     */
    public boolean validateObject(String key, PooledObject<SftpClient> p) {
      return p.getObject().test();
    }

    /**
     * 销毁对象
     */
    public void destroyObject(String key, PooledObject<SftpClient> p) {
      p.getObject().disconnect();
    }
  }

com.funtester.funpool.KeyPoolFactory#destroyObject方法并不是必需的,如果池化的对象除了内存以外不需要额外的资源释放,就不用重写这个方法了。还有一种情况就是对象信息需要清除,比如org.apache.http.client.methods.HttpGet,需要把请求地址和请求头等信息清除,这个需要跟业务需求保持一致。不一定是全都清除。

5. 使用

public class SftpPool {
  
  //池化一组对象用GenericKeyedObjectPool类
  private GenericKeyedObjectPool<String, SftpClient> internalKeyedPool;

  public SftpPool(Map<String, SftpProperties> SftpPropertiesMap, PoolProperties poolProperties) {
    this.internalKeyedPool = new GenericKeyedObjectPool<>(SftpPropertiesMap, getKeyedPoolConfig(poolProperties));
  }

 /**
  * 根据key,在池中借对象
  */
  public SftpClient borrowObject(String key) {
    try {
      internalKeyedPool.borrowObject(key);
    } catch (Exception e) {
      throw new PoolException("COULD_NOT_GET_A_RESOURCE_FROM_THE_POOL");
    }
  }

 /**
  * 根据key,往池中归还对象
  */
  public void returnObject(String key, SftpClient sftpClient) {
    try {
        internalKeyedPool.returnObject(key, sftpClient);
    } catch (Exception e) {
      throw new PoolException("COULD_NOT_GET_A_RESOURCE_FROM_THE_POOL");
    }
    
    ...
  }

转自:
https://www.cnblogs.com/lihw/p/17391562.html