使用commons-pool2实现FTP连接池
GitHub : https://github.com/jayknoxqu/ftp-pool
一. 连接池概述
频繁的建立和关闭连接,会极大的降低系统的性能,而连接池会在初始化的时候会创建一定数量的连接,每次访问只需从连接池里获取连接,使用完毕后再放回连接池,并不是直接关闭连接,这样可以保证程序重复使用同一个连接而不需要每次访问都建立和关闭连接, 从而提高系统性能。
二. commons-pool2介绍
2.1 pool2的引入
<!-- 使用commons-pool2 实现ftp连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.5.0</version>
</dependency> <!-- 引入FTPClient作为池化对象 -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
2.2 pool2的组成
PooledObject(池化对象) PooledObjectFactory(对象工厂) ObjectPool (对象池)
对应为:
FTPClient(池化对象) FTPClientFactory(对象工厂) FTPClientPool(对象池)
关系图:

三. 实现连接池
3.1 配置FtpClient
我们已经有现成的池化对象(FtpClient)了,只需要添加配置即可
@ConfigurationProperties(ignoreUnknownFields = false, prefix = "ftp.client")
public class FtpClientProperties {
// ftp地址
private String host;
// 端口号
private Integer port = 21;
// 登录用户
private String username;
// 登录密码
private String password;
// 被动模式
private boolean passiveMode = false;
// 编码
private String encoding = "UTF-8";
// 连接超时时间(秒)
private Integer connectTimeout;
// 缓冲大小
private Integer bufferSize = 1024;
// 传输文件类型
private Integer transferFileType;
}
application.properties配置为:
ftp.client.host=127.0.0.1
ftp.client.port=22
ftp.client.username=root
ftp.client.password=root
ftp.client.encoding=utf-8
ftp.client.passiveMode=false
ftp.client.connectTimeout=30000
3.2 创建FtpClientFactory
在commons-pool2中有两种工厂:PooledObjectFactory 和KeyedPooledObjectFactory,我们使用前者。
public interface PooledObjectFactory<T> {
//创建对象
PooledObject<T> makeObject();
//激活对象
void activateObject(PooledObject<T> obj);
//钝化对象
void passivateObject(PooledObject<T> obj);
//验证对象
boolean validateObject(PooledObject<T> obj);
//销毁对象
void destroyObject(PooledObject<T> obj);
}
创建FtpClientFactory只需要继承BasePooledObjectFactory这个抽象类 ,而它则实现了PooledObjectFactory
public class FtpClientFactory extends BasePooledObjectFactory<FTPClient> {
private FtpClientProperties config;
public FtpClientFactory(FtpClientProperties config) {
this.config = config;
}
/**
* 创建FtpClient对象
*/
@Override
public FTPClient create() {
FTPClient ftpClient = new FTPClient();
ftpClient.setControlEncoding(config.getEncoding());
ftpClient.setConnectTimeout(config.getConnectTimeout());
try {
ftpClient.connect(config.getHost(), config.getPort());
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
ftpClient.disconnect();
log.warn("FTPServer refused connection,replyCode:{}", replyCode);
return null;
}
if (!ftpClient.login(config.getUsername(), config.getPassword())) {
log.warn("ftpClient login failed... username is {}; password: {}", config.getUsername(), config.getPassword());
}
ftpClient.setBufferSize(config.getBufferSize());
ftpClient.setFileType(config.getTransferFileType());
if (config.isPassiveMode()) {
ftpClient.enterLocalPassiveMode();
}
} catch (IOException e) {
log.error("create ftp connection failed...", e);
}
return ftpClient;
}
/**
* 用PooledObject封装对象放入池中
*/
@Override
public PooledObject<FTPClient> wrap(FTPClient ftpClient) {
return new DefaultPooledObject<>(ftpClient);
}
/**
* 销毁FtpClient对象
*/
@Override
public void destroyObject(PooledObject<FTPClient> ftpPooled) {
if (ftpPooled == null) {
return;
}
FTPClient ftpClient = ftpPooled.getObject();
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
}
} catch (IOException io) {
log.error("ftp client logout failed...{}", io);
} finally {
try {
ftpClient.disconnect();
} catch (IOException io) {
log.error("close ftp client failed...{}", io);
}
}
}
/**
* 验证FtpClient对象
*/
@Override
public boolean validateObject(PooledObject<FTPClient> ftpPooled) {
try {
FTPClient ftpClient = ftpPooled.getObject();
return ftpClient.sendNoOp();
} catch (IOException e) {
log.error("Failed to validate client: {}", e);
}
return false;
}
}
3.3 实现FtpClientPool
在commons-pool2中预设了三个可以直接使用的对象池:GenericObjectPool、GenericKeyedObjectPool和SoftReferenceObjectPool
示列:
GenericObjectPool<FTPClient> ftpClientPool = new GenericObjectPool<>(new FtpClientFactory());
我们也可以自己实现一个连接池:
public interface ObjectPool<T> extends Closeable {
// 从池中获取一个对象
T borrowObject();
// 归还一个对象到池中
void returnObject(T obj);
// 废弃一个失效的对象
void invalidateObject(T obj);
// 添加对象到池
void addObject();
// 清空对象池
void clear();
// 关闭对象池
void close();
}
通过继承BaseObjectPool去实现ObjectPool
public class FtpClientPool extends BaseObjectPool<FTPClient> {
private static final int DEFAULT_POOL_SIZE = 8;
private final BlockingQueue<FTPClient> ftpBlockingQueue;
private final FtpClientFactory ftpClientFactory;
/**
* 初始化连接池,需要注入一个工厂来提供FTPClient实例
*
* @param ftpClientFactory ftp工厂
* @throws Exception
*/
public FtpClientPool(FtpClientFactory ftpClientFactory) throws Exception {
this(DEFAULT_POOL_SIZE, ftpClientFactory);
}
public FtpClientPool(int poolSize, FtpClientFactory factory) throws Exception {
this.ftpClientFactory = factory;
ftpBlockingQueue = new ArrayBlockingQueue<>(poolSize);
initPool(poolSize);
}
/**
* 初始化连接池,需要注入一个工厂来提供FTPClient实例
*
* @param maxPoolSize 最大连接数
* @throws Exception
*/
private void initPool(int maxPoolSize) throws Exception {
for (int i = 0; i < maxPoolSize; i++) {
// 往池中添加对象
addObject();
}
}
/**
* 从连接池中获取对象
*/
@Override
public FTPClient borrowObject() throws Exception {
FTPClient client = ftpBlockingQueue.take();
if (ObjectUtils.isEmpty(client)) {
client = ftpClientFactory.create();
// 放入连接池
returnObject(client);
// 验证对象是否有效
} else if (!ftpClientFactory.validateObject(ftpClientFactory.wrap(client))) {
// 对无效的对象进行处理
invalidateObject(client);
// 创建新的对象
client = ftpClientFactory.create();
// 将新的对象放入连接池
returnObject(client);
}
return client;
}
/**
* 返还对象到连接池中
*/
@Override
public void returnObject(FTPClient client) {
try {
if (client != null && !ftpBlockingQueue.offer(client, 3, TimeUnit.SECONDS)) {
ftpClientFactory.destroyObject(ftpClientFactory.wrap(client));
}
} catch (InterruptedException e) {
log.error("return ftp client interrupted ...{}", e);
}
}
/**
* 移除无效的对象
*/
@Override
public void invalidateObject(FTPClient client) {
try {
client.changeWorkingDirectory("/");
} catch (IOException e) {
e.printStackTrace();
} finally {
ftpBlockingQueue.remove(client);
}
}
/**
* 增加一个新的链接,超时失效
*/
@Override
public void addObject() throws Exception {
// 插入对象到队列
ftpBlockingQueue.offer(ftpClientFactory.create(), 3, TimeUnit.SECONDS);
}
/**
* 关闭连接池
*/
@Override
public void close() {
try {
while (ftpBlockingQueue.iterator().hasNext()) {
FTPClient client = ftpBlockingQueue.take();
ftpClientFactory.destroyObject(ftpClientFactory.wrap(client));
}
} catch (Exception e) {
log.error("close ftp client ftpBlockingQueue failed...{}", e);
}
}
}
不太赞成自己去实现连接池,这样会带来额外的维护成本...
四. 代码地址:
GitHub : https://github.com/jayknoxqu/ftp-pool
五. 参考资料:
commons-pool2 官方案列
使用commons-pool2实现FTP连接池的更多相关文章
- FTP连接池
我们项目使用的是 Apache的(commons-net-3.2.jar) FTPClient,但是系统偶尔会有异常,趁着刚解决完,总结一下. 日志中提示是类似 java.lang.Exception ...
- Java 自定义FTP连接池
转自:https://blog.csdn.net/eakom/article/details/79038590 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn ...
- JAVA ftp连接池功能实现
抽象类: package com.echo.store; import java.util.Enumeration; import java.util.Hashtable; abstract clas ...
- SpringBoot整合自定义FTP文件连接池
说明:通过GenericObjectPool实现的FTP连接池,记录一下以供以后使用环境:JDK版本1.8框架 :springboot2.1文件服务器: Serv-U1.引入依赖 <!--ftp ...
- 存储过程 务的概念 事务的特性 关于异常的处理 连接池 构JdbcUtil类
1 存储过程 1)用当地数据库语言,写的一段业务逻辑算法,并该算法存储在客户端 2)使用存储过程需要用于CallableStatement接口,同时需要使如下SQL命令调用:{call a ...
- JDBC实例--JDBC连接池技术解密,连接池对我们不再陌生
一.为什么我们要用连接池技术? 前面的数据库连接的建立及关闭资源的方法有些缺陷.统舱传统数据库访问方式:一次数据库访问对应一个物理连接,每次操作数据库都要打开.关闭该物理连接, 系统性能严重受损. 解 ...
- Lettuce连接池——解决“MXBean already registered with name org.apache.commons.pool2:type=GenericObjectPool,name=pool”
LettuceConfig: package com.youdao.outfox.interflow.config; import io.lettuce.core.support.Connection ...
- Java--对象池化技术 org.apache.commons.pool2.ObjectPool
org.apache.commons.pool2.ObjectPool提供了对象池,开发的小伙伴们可以直接使用来构建一个对象池 使用该对象池具有两个简单的步骤: 1.创建对象工厂,org.apache ...
- commons.pool2 对象池的使用
commons.pool2 对象池的使用 ? 1 2 3 4 5 <dependency> <groupId>org.apache.commons</groupI ...
随机推荐
- unity2D 船只型物体驱动的实现
船只向前行驶的驱动力 假设在水中没有摩擦阻力,船只有惯性,船只可以转弯,按下前进键时船只会在力的作用下使得自身的物理运动方向变化到自身的前方方向,从而向前行进. 上图中 V:船当前物理速度 V1,V2 ...
- AEAI DP开发统计分析
1 背景概述 平时做统计分析都是调rest服务,给前台提供数据,然后在管理控制台里配置portlet.但并不是所有的项目都会用到portal,这时就需要在AEAI DP应用开发平台里开发统计分析了,下 ...
- 正则表达式中 re.match与re.search的区别
标签: 本文和大家分享的主要是python正则表达式中re.match函数与re.search方法的相关用法及异同点,希望通过本文的分享,能对大家有所帮助. re.match函数 re.match 尝 ...
- [学习笔记]树套树 线段树套Splay
今天调了一个早上哈哈哈,不过因为\(Splay\),常数比较大 洛谷的评测记录: \(Code\ Below:\) #include <bits/stdc++.h> #define ll ...
- [JavaScript] 根据指定宽度截取字符串
/** * 根据指定宽度截取字符串 * @param desc 原始字符串 * @param width 该显示的宽度 * @param fontsize 字体大小 12px * @returns { ...
- 【bug】VUE:Cannot read property '_withTask' of undefined
如题 成因:极大可能是template上有某个函数,没有在 methods中声明导致的. 解决:找到那个未声明的函数名,写在methods中.你可以使用二分法快速找到.
- SQL Server 用户'NT AUTHORITY\IUSR' 登录失败
今天打开网站时,突然报这个错误,平时都好好的 Cannot open database "JMECC" requested by the login. The login fail ...
- JavaScript 的 Async\/Await 完胜 Promise 的六
参考:http://www.10tiao.com/html/558/201705/2650964601/1.html Node 现在从版本 7.6 开始就支持 async/await 了. 简介: A ...
- day 39 jq 学习入门2
---恢复内容开始--- 前情提要: jq 是用来降低js 的工作的一个组件 一:利用jq 实现动画效果 <!DOCTYPE html> <html lang="en&qu ...
- D16——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D16 20180927内容纲要: 1.JavaScript介绍 2.JavaScript功能介绍 3.JavaScript变量 4.Dom操作 a.获取标签 b ...