使用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 ...
随机推荐
- Django(ORM查询1)
day69 参考:http://www.cnblogs.com/liwenzhou/p/8660826.html 在Python脚本中调用Django环境 orm1.py import os if _ ...
- 【kuangbin专题】计算几何_凸包
1.poj1113 Wall 题目:http://poj.org/problem?id=1113 题意:用一条线把若干个点包起来,并且线距离任何一个点的距离都不小于r.求这条线的最小距离是多少? 分析 ...
- 关于axios及其在vue中的配置
什么是axios?官方解释:axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中. 具有以下特点: 从浏览器中创建 XMLHttpRequests 从 nod ...
- postgresql-递增uuid优点
递增uuid的优点: https://blog.2ndquadrant.com/on-the-impact-of-full-page-writes/ 减小wal生成
- dubbo+zookeeper注册服务报错问题:No service registed on zookeeper
2019-04-04 11:23:40,372 ERROR [tomcat-threads--1] (com.bill99.dolphin.controller.ExceptionController ...
- 使用Express构建RESTful API
RESTful服务 REST(Representational State Transfer)的意思是表征状态转移,它是一种基于HTTP协议的网络应用接口风格,充分利用HTTP的方法实现统一风格接口的 ...
- [原创]K8飞刀20160613 Plesk密码 & 注册表16进制转换 & Html实体解密
K8飞刀 by K8拉登哥哥@[K8搞基大队]博客: http://qqhack8.blog.163.com 简介: K8飞刀是一款多功能的安全测试工具. Hacker Swiss Army Kni ...
- [0day]微软XP系统右键菜单任意DLL却持
作者:K8哥哥只要在DLL上右键就被却持 任意DLL名称 任意位置 (其实是EXPLOR) 这个漏洞早已存在,08年的时候就发现了(当时编译某个DLL源码) 在DLL上右键看属性的时候崩溃了,当时就想 ...
- 基于alpine用dockerfile创建的爬虫Scrapy镜像
一.下载alpine镜像 [root@DockerBrian ~]# docker pull alpine Using default tag: latest Trying to pull repos ...
- 集合框架_DAY15
1:集合(掌握) (1)集合的由来 我们需要对多个对象进行存储和获取.可以使用对象数组.但是,如果对象的个数是变化的,对象数组就解决不了了.怎么办呢?java就提供了集合类解决. (2)集合和数组的区 ...