​ 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连接池的更多相关文章

  1. FTP连接池

    我们项目使用的是 Apache的(commons-net-3.2.jar) FTPClient,但是系统偶尔会有异常,趁着刚解决完,总结一下. 日志中提示是类似 java.lang.Exception ...

  2. Java 自定义FTP连接池

    转自:https://blog.csdn.net/eakom/article/details/79038590 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn ...

  3. JAVA ftp连接池功能实现

    抽象类: package com.echo.store; import java.util.Enumeration; import java.util.Hashtable; abstract clas ...

  4. SpringBoot整合自定义FTP文件连接池

    说明:通过GenericObjectPool实现的FTP连接池,记录一下以供以后使用环境:JDK版本1.8框架 :springboot2.1文件服务器: Serv-U1.引入依赖 <!--ftp ...

  5. 存储过程 务的概念 事务的特性 关于异常的处理 连接池 构JdbcUtil类

    1 存储过程    1)用当地数据库语言,写的一段业务逻辑算法,并该算法存储在客户端    2)使用存储过程需要用于CallableStatement接口,同时需要使如下SQL命令调用:{call a ...

  6. JDBC实例--JDBC连接池技术解密,连接池对我们不再陌生

    一.为什么我们要用连接池技术? 前面的数据库连接的建立及关闭资源的方法有些缺陷.统舱传统数据库访问方式:一次数据库访问对应一个物理连接,每次操作数据库都要打开.关闭该物理连接, 系统性能严重受损. 解 ...

  7. 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 ...

  8. Java--对象池化技术 org.apache.commons.pool2.ObjectPool

    org.apache.commons.pool2.ObjectPool提供了对象池,开发的小伙伴们可以直接使用来构建一个对象池 使用该对象池具有两个简单的步骤: 1.创建对象工厂,org.apache ...

  9. commons.pool2 对象池的使用

    commons.pool2 对象池的使用 ? 1 2 3 4 5 <dependency>     <groupId>org.apache.commons</groupI ...

随机推荐

  1. sharepoint support ashx file

    Hello, I did the steps from the tutorial you are using. I have received the same error when I did no ...

  2. [C++基础]队列<queue>中的常用函数

    在C++中只要#include<queue>即可使用队列类,其中在面试或笔试中常用的成员函数如下(按照最常用到不常用的顺序) 1. push 2. pop 3. size 4. empty ...

  3. js判断是否手机自动跳转移动端

    写法一: {literal} <script> //判断是否手机自动跳转 var browser={versions:function(){var u=navigator.userAgen ...

  4. CE修改器使用教程 [基础篇]

    Cheat Engine  是一款内存修改编辑工具 ,它允许你修改你的游戏或软件内存数据,以得到一些其他功能.它包括16进制编辑,反汇编程序,内存查找工具.与同类修改工具相比,它具有强大的反汇编功能, ...

  5. opencv2.4.13.7的resize函数使用(c++)

    先来看一下resize函数的原型,如下. C++: void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, doub ...

  6. 解决白屏(vue) - webpace es6转es5

    1.npm安装 npm install babel-polyfillnpm install es6-promise package.json中会出现 "babel-polyfill" ...

  7. Python数据结构:栈 队列

    栈,队列的特性: 1.栈(stacks)是一种只能通过访问其一端来实现数据存储与检索的线性数据结构,具有后进先出(last in first out,LIFO)的特征 2.队列(queue)是一种具有 ...

  8. JobScheduler android任务调度处理组件(类似QuartZ)

    JobScheduler是Android L(API21)新增的特性,用于定义满足某些条件下(电量,网络,时间,屏幕熄/亮 ,设备是否空闲 等)执行的任务.它的宗旨是把一些不是特别紧急的任务放到更合适 ...

  9. (转)pathlib路径库使用详解

    原文:https://xin053.github.io/2016/07/03/pathlib%E8%B7%AF%E5%BE%84%E5%BA%93%E4%BD%BF%E7%94%A8%E8%AF%A6 ...

  10. Linux驱动:I2C驱动编写要点

    继续上一篇博文没讲完的内容“针对 RepStart 型i2c设备的驱动模型”,其中涉及的内容有:i2c_client 的注册.i2c_driver 的注册.驱动程序的编写. 一.i2c 设备的注册分析 ...