我们项目使用的是 Apache的(commons-net-3.2.jar) FTPClient,但是系统偶尔会有异常,趁着刚解决完,总结一下。

日志中提示是类似

java.lang.Exception: FTP response 421 received.  Server closed connection.

上网上一查,说是FTP服务端连接数已满了,出现这种问题一般是,部分连接占用的时间太久,导致新连接获取不到,从而抛出的异常,找到问题就方便下手了(^_^);

我的思路是,既然连接会出问题,那就从这里入手,

  1. 使用FTP连接池,管理连接
  2. 初始化连接池,设置超时时间,
  3. 连接数满时,等待连接释放

我的连接池这一块采用的也是Apache的(commons-pool2-2.2.jar)GenericObjectPool,大概有一下几个类

  1. 配置类

    package com.i1stcs.mvs.utils.ftp;
    
    //FTPClient配置类
    class FTPConfig {
    
        private String host;
        private int port;
        private String username;
        private String password;
        private String encoding;
        private String workPath;
    
        private int maxTotal;
        private int maxWaitMillis;
    
        public String getHost() {
            return host;
        }
    
        public void setHost(String host) {
            this.host = host;
        }
    
        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 String getEncoding() {
            return encoding;
        }
    
        public void setEncoding(String encoding) {
            this.encoding = encoding;
        }
    
        public String getWorkPath() {
            return workPath;
        }
    
        public void setWorkPath(String workPath) {
            this.workPath = workPath;
        }
    
        public int getMaxTotal() {
            return maxTotal;
        }
    
        public void setMaxTotal(int maxTotal) {
            this.maxTotal = maxTotal;
        }
    
        public int getMaxWaitMillis() {
            return maxWaitMillis;
        }
    
        public void setMaxWaitMillis(int maxWaitMillis) {
            this.maxWaitMillis = maxWaitMillis;
        }
    
    }
  2. 连接池实现类
    package com.i1stcs.mvs.utils.ftp;
    
    import org.apache.commons.net.ftp.FTPClient;
    import org.apache.commons.pool2.impl.GenericObjectPool;
    import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.i1stcs.mvs.utils.PropertiesUtil;
    
    public class FTPPool {
    
        private static final GenericObjectPool<FTPClient> internalPool;
        private static Logger logger = LoggerFactory.getLogger(FTPPool.class);
    
        static {
            String ip = PropertiesUtil.getValue("ftp.server.ip", "");
            String userName = PropertiesUtil.getValue("ftp.server.username", "");
            String password = PropertiesUtil.getValue("ftp.server.password", "");
            String encoding = PropertiesUtil.getValue("ftp.server.encoding", "UTF-8");
            int port = PropertiesUtil.getValue("ftp.server.port", 21);
    
            int maxTotal = PropertiesUtil.getValue("ftp.server.maxTotal", 8);
            int maxWaitMillis = PropertiesUtil.getValue("ftp.server.maxWaitMillis", 30_000);
    
            FTPConfig config = new FTPConfig();
            config.setEncoding(encoding);
            config.setHost(ip);
            config.setUsername(userName);
            config.setPassword(password);
            config.setPort(port);
            config.setMaxTotal(maxTotal);
            config.setMaxWaitMillis(maxWaitMillis);
    
            GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
            poolConfig.setMaxTotal(config.getMaxTotal());// 不设置的话默认是8
            poolConfig.setMaxWaitMillis(config.getMaxWaitMillis());// 不设置默认无限等待
    
            internalPool = new GenericObjectPool<FTPClient>(new FTPPoolFactory(config), poolConfig);
        }
    
        /**
         * 获取资源
         *
         * @return
         */
        public static FTPClient getFTPClient() {
            try {
                return internalPool.borrowObject();
            } catch (Exception e) {
                logger.error("获取FTP连接异常:", e);
                return null;
            }
        }
    
        /**
         * 归还资源
         *
         * @param ftpClient
         */
        public static void returnFTPClient(FTPClient ftpClient) {
            try {
                internalPool.returnObject(ftpClient);
            } catch (Exception e) {
                logger.error("释放FTP连接异常:", e);
            }
        }
    
        /**
         * 销毁池子
         */
        public static void destroy() {
            try {
                internalPool.close();
            } catch (Exception e) {
                logger.error("销毁FTP数据源异常:", e);
            }
        }
    
    }
  3. 连接池工厂类
    package com.i1stcs.mvs.utils.ftp;
    
    import java.io.IOException;
    import java.net.SocketException;
    
    import org.apache.commons.net.ftp.FTP;
    import org.apache.commons.net.ftp.FTPClient;
    import org.apache.commons.net.ftp.FTPClientConfig;
    import org.apache.commons.net.ftp.FTPReply;
    import org.apache.commons.pool2.PooledObject;
    import org.apache.commons.pool2.PooledObjectFactory;
    import org.apache.commons.pool2.impl.DefaultPooledObject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    class FTPPoolFactory implements PooledObjectFactory<FTPClient> {
    
        private static Logger logger = LoggerFactory.getLogger(FTPPoolFactory.class);
    
        private FTPConfig ftpConfig;
        private static int BUFF_SIZE = 256000;
    
        public FTPPoolFactory(FTPConfig ftpConfig) {
            this.ftpConfig = ftpConfig;
        }
    
        @Override
        public PooledObject<FTPClient> makeObject() throws Exception {
            FTPClient ftpClient = new FTPClient();
            ftpClient.setDefaultPort(ftpConfig.getPort());
            ftpClient.setConnectTimeout(30000);
            ftpClient.setDataTimeout(180000);
            ftpClient.setControlKeepAliveTimeout(60);
            ftpClient.setControlKeepAliveReplyTimeout(60);
            ftpClient.setControlEncoding(ftpConfig.getEncoding());
    
            FTPClientConfig clientConfig = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
            clientConfig.setServerLanguageCode(ftpConfig.getEncoding());
            ftpClient.configure(clientConfig);
    
            try {
                ftpClient.connect(ftpConfig.getHost());
            } catch (SocketException exp) {
                logger.warn("connect timeout with FTP server:" + ftpConfig.getHost());
                throw new Exception(exp.getMessage());
            } catch (IOException exp) {
                logger.warn("connect FTP server:" + ftpConfig.getHost() + " meet error:" + exp.getMessage());
                throw new Exception(exp.getMessage());
            }
    
            int reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
                logger.error("FTPServer refused connection");
                return null;
            }
            boolean result = ftpClient.login(ftpConfig.getUsername(), ftpConfig.getPassword());
            if (!result) {
                logger.warn("FTP server refused refused connection.");
                throw new Exception(
                        "login failed with FTP server:" + ftpConfig.getHost() + " may user name and password is wrong");
            }
            ftpClient.setBufferSize(BUFF_SIZE);
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            ftpClient.setFileTransferMode(FTP.COMPRESSED_TRANSFER_MODE);
    
            ftpClient.changeWorkingDirectory(ftpConfig.getWorkPath());
    
            return new DefaultPooledObject<FTPClient>(ftpClient);
        }
    
        @Override
        public void destroyObject(PooledObject<FTPClient> pooledObject) throws Exception {
            FTPClient ftpClient = pooledObject.getObject();
            try {
                ftpClient.logout();
                if (ftpClient.isConnected()) {
                    ftpClient.disconnect();
                }
            } catch (IOException e) {
                throw new RuntimeException("Could not disconnect from server.", e);
            }
        }
    
        @Override
        public boolean validateObject(PooledObject<FTPClient> pooledObject) {
            FTPClient ftpClient = pooledObject.getObject();
            try {
                return ftpClient.sendNoOp();//
            } catch (IOException e) {
                logger.error("Failed to validate client:", e);
                return false;
            }
        }
    
        @Override
        public void activateObject(PooledObject<FTPClient> p) throws Exception {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public void passivateObject(PooledObject<FTPClient> p) throws Exception {
            // TODO Auto-generated method stub
    
        }
    
    }
  4. FTP工具类
    package com.songyz.ftp;
    
    import org.apache.commons.net.ftp.FTPClient;
    import org.apache.commons.net.ftp.FTPFile;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.i1stcs.mvs.utils.PropertiesUtil;
    
    public class FTPHelper {
        private static final Logger log = LoggerFactory.getLogger(FTPHelper.class);
    
        public static FTPCommand createDelDirCmd(String remotePath) {
            return new DeleteDirCommand(remotePath);
        }
    
        public static FTPCommand createDelFileCmd(String remoteFile) {
            return new DeleteFileCommand(remoteFile);
        }
    
        public static void execute(FTPCommand... cmds) throws Exception {
            FTPClient client = null;
            try {
                client = FTPPool.getFTPClient();
                String mode = PropertiesUtil.getValue("ftp.server.mode", "localactive");
                String workpath = PropertiesUtil.getValue("ftp.server.workpath", "./");
                for (FTPCommand cmd : cmds) {
                    client.changeWorkingDirectory(workpath);
                    if (mode.equals("localactive"))
                        client.enterLocalActiveMode();
                    else if (mode.equals("localpassive"))
                        client.enterLocalPassiveMode();
                    else
                        client.enterRemotePassiveMode();
    
                    cmd.isOK = cmd.doAction(client);
                    if (cmd.isOK == false) {
                        if (cmd.error == null || cmd.error.isEmpty()) {
                            String clsName = cmd.getClass().getName();
                            cmd.error = "execute command:" + clsName.substring(clsName.lastIndexOf("$") + 1) + " failed -- "
                                    + client.getReplyString();
                        }
                        throw new Exception(cmd.error);
                    }
                }
            } finally {
                FTPPool.returnFTPClient(client);
            }
        }
    
        public static abstract class FTPCommand {
            protected boolean isOK = false;
            protected String error = "";
    
            public abstract boolean doAction(FTPClient client);
    
            public boolean isSuccessed() {
                return isOK;
            }
    
            public String getError() {
                return error;
            }
        }
    
        static class DeleteDirCommand extends FTPCommand {
            private String remotePath;
    
            public DeleteDirCommand(String remotePath) {
                this.remotePath = remotePath;
            }
    
            @Override
            public boolean doAction(FTPClient client) {
                try {
                    FTPFile[] files = client.listFiles(remotePath);
                    if (files == null || files.length == 0)
                        return client.removeDirectory(remotePath);
    
                    for (FTPFile file : files) {
                        if (file.isDirectory())
                            deleteChildren(client, remotePath + "/" + file.getName());
                        else
                            client.dele(remotePath + "/" + file.getName());
                    }
                    return client.removeDirectory(remotePath);
                } catch (Exception exp) {
                    error = "failed to delete dirctory " + remotePath + " in FTP server because " + exp.getMessage();
                    log.warn(error, exp);
                    return false;
                }
            }
    
            private boolean deleteChildren(FTPClient client, String pathName) {
                try {
                    FTPFile[] files = client.listFiles(pathName);
                    if (files == null || files.length == 0)
                        return client.removeDirectory(pathName);
    
                    for (FTPFile file : files) {
                        if (file.isDirectory())
                            deleteChildren(client, pathName + "/" + file.getName());
                        else
                            client.dele(pathName + "/" + file.getName());
                    }
                    return client.removeDirectory(pathName);
                } catch (Exception exp) {
                    error = "failed to delete dirctory " + remotePath + "in FTP server because " + exp.getMessage();
                    log.warn(error, exp);
                    return false;
                }
            }
        }
    
        static class DeleteFileCommand extends FTPCommand {
            private String remoteFilePath;
    
            public DeleteFileCommand(String remoteFilePath) {
                this.remoteFilePath = remoteFilePath;
            }
    
            @Override
            public boolean doAction(FTPClient client) {
                try {
                    FTPFile[] files = client.listFiles(remoteFilePath);
                    if (files == null || files.length == 0)
                        return true;
                    else
                        return client.deleteFile(remoteFilePath);
                } catch (Exception exp) {
                    error = "failed to delete file " + remoteFilePath + " in FTP server because " + exp.getMessage();
                    log.warn(error, exp);
                    return false;
                }
            }
        }
    
    }

    特别提示:PropertiesUtil这个类是我自己读取配置文件的类,就不贴代码了,

    至此,大功告成!!!

FTP连接池的更多相关文章

  1. 使用commons-pool2实现FTP连接池

    ​ GitHub : https://github.com/jayknoxqu/ftp-pool 一. 连接池概述 ​ 频繁的建立和关闭连接,会极大的降低系统的性能,而连接池会在初始化的时候会创建一定 ...

  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. 连接SQLServer时,因启用连接池导致孤立事务的原因分析和解决办法

    本文出处:http://www.cnblogs.com/wy123/p/6110349.html 之前遇到过这么一种情况: 连接数据库的部分Session会出现不定时的阻塞,这种阻塞时长时短,有时候持 ...

  6. C3p0连接池配置

    在Java开发中,使用JDBC操作数据库的四个步骤如下:   ①加载数据库驱动程序(Class.forName("数据库驱动类");)   ②连接数据库(Connection co ...

  7. Java第三方数据库连接池库-DBCP-C3P0-Tomcat内置连接池

    连接池原理 数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”.预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去.我们可以通过设定连接池 ...

  8. common-pool2 学习:thrift连接池的另一种实现

    对象池是一种很实用的技术,经典的例子就是数据库连接池.去年曾经从零开始写过一个thrift客户端连接池.如果不想重造轮子,可以直接在apache开源项目commons-pool的基础上开发. 步骤: ...

  9. druid连接池获取不到连接的一种情况

    数据源一开始配置: jdbc.initialSize=1jdbc.minIdle=1jdbc.maxActive=5 程序运行一段时间后,执行查询抛如下异常: exception=org.mybati ...

随机推荐

  1. IBM RAD 快捷键

    Ctrl+1         快速修复(最经典的快捷键,就不用多说了) Ctrl+D:        删除当前行 Ctrl+Alt+↓    复制当前行到下一行(复制增加) Ctrl+Alt+↑    ...

  2. 怎么分别javascript写在<head>里还是<body>里面?

    怎么分别javascript写在<head>里还是<body>里面? 具体哪些语句写在<body>里,哪些语句写在<head>里 满意答案 BeginN ...

  3. Ibatis和Hibernate的比较

    Ibatis和Hibernate的比较 分类: IBATIS HIBERNATE2010-11-19 17:58 341人阅读 评论(0) 收藏 举报 hibernateibatis数据库sqlcac ...

  4. linux利用命令重置大量密码

     yum -y install expectmkpasswd -l 10 -v was | grep 'is *'  >> 123.txtmkpasswd -l 10 -v logv |  ...

  5. andorid下从相册选取/拍照选取一张相片并剪切

    http://www.2cto.com/kf/201401/270144.html 在Android编程中,从相册选取或是拍照选取一张照片然后对其进行剪切的需求非常的多 之前的一篇文章只说到如何从相册 ...

  6. 基于Python的数据分析(2):字符串编码

    在上一篇文章<基于Python的数据分析(1):配置安装环境>中的第四个步骤中我们在python的启动步骤中强制要求加载sitecustomize.py文件并设置其默认编码为"u ...

  7. AE、AS调用时用代码提供许可(不需要添加LicenseControl控件)

    private void CheckBindLicense() { ESRI.ArcGIS.RuntimeManager.Bind(ESRI.ArcGIS.ProductCode.EngineOrDe ...

  8. java运行机制、Jdk版本及Java环境变量

    一.语言特性 计算机高级语言按程序的执行方式可分为:编译型和解释型两种.编译型的语言是指使用专门的编译器,针对特定的平台(操作系统)一次性翻译成被该平台硬件执行的机器码,并包装成该平台可执行性程序文件 ...

  9. 和同门一起做的PHP网站

    用的是PHP语言开发,使用ThinkPHP框架,数据库是mysql  统计是用的站长统计,前端很多都是别人网 上扒下来的.css.js水平不高啊.服务器是香港恒创科技的,性价比还可以哦,而且还免备案. ...

  10. Cocos2d-x 实战

    跨平台商业项目实战:攻城大作战游戏创意触发点:做什么样的游戏?分析当前主流的游戏:经典游戏(俄罗斯方块).大众化的游戏(卡牌游戏.休闲游戏).重口味游戏. 游戏创意:生活当中 游戏开发流程:1.策划方 ...