利用commons-pool2自定义对象池
参考地址:https://blog.csdn.net/qq447995687/article/details/80233621
https://zhuanlan.zhihu.com/p/36216932
1. 相关概念:
链接池/对象池(ObjectPool):用于存放链接对象的一个池子(集合),通常用数组或者List对象.durid用两个数组分别保存活跃的链接和空闲链接.commons-pool2用双端阻塞队列LinkedBlockingDeque保存空闲链接
用ConcurrentHashMap保存所有的链接.
对象工厂(PooledObjectFactory):连接池工厂,用于产生一个新的链接对象.
链接对象/池中对象(PooledObject):链接池里面存放的对象.
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
2.相关API
PooledObjectFactory
PooledObjectFactory用于生成连接对象的工厂接口。该接口包含以下功能:
①产生一个连接对象:
PooledObject<T> makeObject() throws Exception;
以ftp连接为例:
1 /** 2 * 创建连接到池中 3 * @return 4 * @throws Exception 5 */ 6 @Override 7 public PooledObject<FTPClient> makeObject() throws Exception { 8 FTPClient ftpClient = new FTPClient();//创建客户端实例 9 return new DefaultPooledObject<>(ftpClient); 10 }
在连接池初始化时初始化最小连接数 ;
驱逐线程驱逐完过期连接后池中连接数<最小连接数,需重新生成连接,使连接数达到池中最小连接数 ;
获取新的连接时,池中连接对象均被占用,但当前连接数<总连接数时 ;
一般当遇到以上3中情况时需要调用该方法产生一个新的连接
②销毁一个连接对象:
void destroyObject(PooledObject<T> p) throws Exception;
1 /** 2 * 销毁连接,当连接池空闲数量达到上限时,调用此方法销毁连接 3 * @param pooledObject 4 * @throws Exception 5 */ 6 @Override 7 public void destroyObject(PooledObject<FTPClient> pooledObject) throws Exception { 8 FTPClient ftpClient = pooledObject.getObject(); 9 try { 10 ftpClient.logout(); 11 if (ftpClient.isConnected()) { 12 ftpClient.disconnect(); 13 } 14 } catch (IOException e) { 15 throw new RuntimeException("Could not disconnect from server.", e); 16 } 17 }
调用该方法销毁一个连接对象。对于实现这个方法来说非常重要的是要考虑到处理异常情况,另外实现必须考虑一个实例如果与垃圾回收器失去联系那么永远不会被销毁。
③校验方法
boolean validateObject(PooledObject<T> p);
1 /** 2 * 链接状态检查 3 * @param pooledObject 4 * @return 5 */ 6 @Override 7 public boolean validateObject(PooledObject<FTPClient> pooledObject) { 8 FTPClient ftpClient = pooledObject.getObject(); 9 try { 10 return ftpClient.sendNoOp(); 11 } catch (IOException e) { 12 return false; 13 } 14 }
此方法主要用于校验一个连接是否可用,比如在borrow一个连接时或者return一个连接时,调用该方法检测连接是否可用。需要注意的是校验方法只会作用于激活的对象实例上。通常的做法是在连接对象空闲的时候进行校验,而不是在使用的时候进行校验,因为这样会影响性能。
④重新激活一个对象
void activateObject(PooledObject<T> p) throws Exception;
1 /** 2 * 初始化连接 3 * @param pooledObject 4 * @throws Exception 5 */ 6 @Override 7 public void activateObject(PooledObject<FTPClient> pooledObject) throws Exception { 8 FTPClient ftpClient = pooledObject.getObject(); 9 ftpClient.connect(host,port); 10 ftpClient.login(userName, password); 11 ftpClient.setControlEncoding(encoding); 12 ftpClient.changeWorkingDirectory(workDir); 13 ftpClient.setFileType(FTP.BINARY_FILE_TYPE);//设置上传文件类型为二进制,否则将无法打开文件 14 ftpClient.enterLocalPassiveMode(); 15 }
激活一个对象,在向对象池归还被钝化过的对象时调用该方法。
⑤钝化一个对象
void passivateObject(PooledObject<T> p) throws Exception;
1 /** 2 * (卸载)钝化连接,使链接变为可用状态 3 * @param pooledObject 4 * @throws Exception 5 */ 6 @Override 7 public void passivateObject(PooledObject<FTPClient> pooledObject) throws Exception { 8 FTPClient ftpClient = pooledObject.getObject(); 9 try { 10 ftpClient.changeWorkingDirectory(root); 11 ftpClient.logout(); 12 if (ftpClient.isConnected()) { 13 ftpClient.disconnect(); 14 } 15 } catch (IOException e) { 16 throw new RuntimeException("Could not disconnect from server.", e); 17 } 18 }
钝化一个对象。在向对象池归还一个对象是会调用这个方法。
当一个对象从借用到归还需经过如下流程:
初始化连接池,并获取连接
1 /** 2 * author:YZH 3 * time: 2019/6/25 17:06 4 * company: 重庆知行宏图科技有限公司 5 * description: ftp连接池 6 **/ 7 @Component 8 public class FtpPool { 9 10 private final GenericObjectPool<FTPClient> internalPool; 11 12 //初始化连接池 13 public FtpPool(@Autowired FtpClientFactory factory){ 14 GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); 15 poolConfig.setMaxTotal(factory.getMaxTotal()); 16 poolConfig.setMinIdle(factory.getMinIdel()); 17 poolConfig.setMaxIdle(factory.getMaxIdel()); 18 poolConfig.setMaxWaitMillis(factory.getMaxWaitMillis()); 19 //防止获取到不可用的对象 20 poolConfig.setTestOnReturn(true); 21 poolConfig.setTimeBetweenEvictionRunsMillis(10000); 22 poolConfig.setNumTestsPerEvictionRun(2); 23 this.internalPool = new GenericObjectPool<>(factory,poolConfig); 24 } 25 26 //从连接池中取连接 27 public FTPClient getFTPClient() { 28 try { 29 return internalPool.borrowObject(); 30 } catch (Exception e) { 31 e.printStackTrace(); 32 return null; 33 } 34 } 35 //将链接归还到连接池 36 public void returnFTPClient(FTPClient ftpClient) { 37 try { 38 internalPool.returnObject(ftpClient); 39 } catch (Exception e) { 40 e.printStackTrace(); 41 } 42 } 43 /** 44 * 销毁池子 45 */ 46 public void destroy() { 47 try { 48 internalPool.close(); 49 } catch (Exception e) { 50 e.printStackTrace(); 51 } 52 } 53 }