最新在工作当中使用到了
ftp
服务器,我是使用org.apache.commons.net.ftp.FTPClient
做的连接,但是在测试调用的时候经常报断开的管道
错误,就此问题我做了一些分析和总结,是因为在调用的时候我只new
了一个对象,而使用了多线程进行调用,也就是同一个对象进行了多次调用,而每次调用都有调disconnect()
方法,所有再调用的时候就报了断开的管道
错误。
- 针对上面的问题,我首先想到了使用连接池来避免这种底层的错误,而
FTPClient
没有提供连接池,就此我就手工写了一个连接池。 - 下面是实现代码,总共用三个类,在实际使用的时候需要根据业务场景做一些轻微的修改。
FTPClientConnectPool
:FTPClinet 连接池抽象类FTPClientConnectPoolImpl
:FTPClient 连接池具体实现类ConnectTest
:连接测试类
package com.jin.demo.lock;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import java.io.IOException;
import java.util.LinkedList;
/**
* FTPClinet 连接池抽象类
*
* @auther jinsx
* @date 2019-01-02 10:24
*/
public abstract class FTPClientConnectPool {
/**
* 默认最小连接数
*/
protected int initCount = 3;
/**
* 默认最大连接数
*/
protected int initMaxCount = 5;
/**
* 当前正在使用的连接数
*/
protected int connectCount = 0;
/**
* 获取连接默认等待超时时间
*/
protected Long waitTime = 10000L;
/**
* 获取锁默认等待超时时间,防止死锁
*/
protected Long waitTimeLock = 30000L;
/**
* hostname
*/
protected String hostname = "192.168.0.100";
/**
* port
*/
protected int port = 21;
/**
* username
*/
protected String username = "jin01";
/**
* password
*/
protected String password = "123456";
/**
* ftp默认工作目录
*/
protected String path = "/";
/**
* 定义存放FTPClient的连接池
*/
protected LinkedList<FTPClient> pool = new LinkedList<>();
/**
* 获取FTPClient连接对象
*
* @return
*/
protected abstract FTPClient getFTPClient();
/**
* 释放FTPClient连接对象
*
* @param ftp
*/
protected abstract void releaseFTPClient(FTPClient ftp);
/**
* 初始化FTPClient连接对象
*
* @return
*/
protected FTPClient initFTPClient() {
FTPClient ftp = new FTPClient();
try {
ftp.connect(hostname, port);
if(!FTPReply.isPositiveCompletion(ftp.getReplyCode())){
ftp.disconnect();
System.out.println("FTPServer refused connection");
}
boolean login = ftp.login(username, password);
if(!login){
ftp.disconnect();
System.out.println("FTPServer login failed");
}
ftp.setControlEncoding("UTF-8");
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
ftp.changeWorkingDirectory(path);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("initFTPClient result : " + ftp.getReplyString());
return ftp;
}
/**
* 销毁FTPClient连接对象
*
* @param ftp
*/
protected void destroyFTPClient(FTPClient ftp) {
try {
if (ftp != null && ftp.isConnected()) {
ftp.logout();
}
} catch (IOException io) {
io.printStackTrace();
} finally {
// 注意,一定要在finally代码中断开连接,否则会导致占用ftp连接情况
try {
if (ftp != null) {
ftp.disconnect();
}
} catch (IOException io) {
io.printStackTrace();
}
}
}
/**
* 验证FTPClient是否可用
* @param ftp
* @return
*/
protected boolean validateFTPClient(FTPClient ftp) {
try {
return ftp.sendNoOp();
} catch (IOException e) {
throw new RuntimeException("Failed to validate client: " + e, e);
}
}
public int getInitCount() {
return initCount;
}
public void setInitCount(int initCount) {
this.initCount = initCount;
}
public int getInitMaxCount() {
return initMaxCount;
}
public void setInitMaxCount(int initMaxCount) {
this.initMaxCount = initMaxCount;
}
public int getConnectCount() {
return connectCount;
}
public void setConnectCount(int connectCount) {
this.connectCount = connectCount;
}
public Long getWaitTime() {
return waitTime;
}
public void setWaitTime(Long waitTime) {
this.waitTime = waitTime;
}
public Long getWaitTimeLock() {
return waitTimeLock;
}
public void setWaitTimeLock(Long waitTimeLock) {
this.waitTimeLock = waitTimeLock;
}
public String getHostname() {
return hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public LinkedList<FTPClient> getPool() {
return pool;
}
public void setPool(LinkedList<FTPClient> pool) {
this.pool = pool;
}
}
package com.jin.demo.lock;
import org.apache.commons.net.ftp.FTPClient;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* FTPClient 连接池具体实现类
*
* @auther jinsx
* @date 2019-01-02 10:46
*/
public class FTPClientConnectPoolImpl extends FTPClientConnectPool {
private static final FTPClientConnectPoolImpl FTP_CLIENT_CONNECT_POOL = new FTPClientConnectPoolImpl();
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
/***
* 私有构造器,并初始化FTPClient连接池
*/
private FTPClientConnectPoolImpl() {
for (int i = 0; i < super.initCount; i++) {
FTPClient ftp = super.initFTPClient();
super.pool.addLast(ftp);
}
}
/**
* 提供对外获取实例的静态方法
*
* @return
*/
public static FTPClientConnectPoolImpl getInstance() {
return FTP_CLIENT_CONNECT_POOL;
}
@Override
protected FTPClient getFTPClient() {
lock.lock();
try {
// 1.计算超时结束时间
Long endTime = System.currentTimeMillis() + waitTime;
while (endTime >= System.currentTimeMillis()) {
if (super.pool.isEmpty()) {
// 2.连接池中没有连接对象,进入等待
System.out.println(Thread.currentThread().getName() + "进入等待");
condition.await(endTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
if (!super.pool.isEmpty()) {
// 3.从连接池中取出一个连接对象
FTPClient ftp = pool.removeFirst();
// 4.验证是否可用
if (super.validateFTPClient(ftp)) {
return ftp;
} else {
// 5.销毁不可用连接对象
super.destroyFTPClient(ftp);
// 6.重新创建一个连接对象
return super.initFTPClient();
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
// 4.返回null或抛出异常
System.out.println(Thread.currentThread().getName() + "获取超时返回null");
return null;
}
@Override
protected void releaseFTPClient(FTPClient ftp) {
lock.lock();
try {
// 1.如果连接池没有满,并且ftp可用,则放到连接池中
if (super.pool.size() < super.initCount && ftp != null && super.validateFTPClient(ftp)) {
ftp.changeWorkingDirectory(super.path);
super.pool.addLast(ftp);
} else {
super.destroyFTPClient(ftp);
}
// 2.如果连接池有连接,则唤醒一个等待
if (!super.pool.isEmpty()) {
condition.signal();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
package com.jin.demo.lock;
import org.apache.commons.net.ftp.FTPClient;
/**
* 连接测试类
* @auther jinsx
* @date 2019-01-02 09:10
*/
public class ConnectTest {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "前" + System.currentTimeMillis());
FTPClientConnectPoolImpl pool = FTPClientConnectPoolImpl.getInstance();
FTPClient ftp = pool.getFTPClient();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "后" + System.currentTimeMillis() + ftp);
pool.releaseFTPClient(ftp);
}
}).start();
}
}
}