ftp连接池的实现


前言

FTPClient经常销毁和重新生成会很耗资源,因此需要创建ftp连接池,使用完ftp连接后归还给连接池。


一、引入依赖

  <!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.8.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.9.0</version>
</dependency>

二、线程池实现

1、配置类

@EnableConfigurationProperties
@ConfigurationProperties(prefix = "ftp.client")
@Component
@Data
public class FtpPoolConfig extends GenericObjectPoolConfig {
    private String host;
    private int port = 21;
    private String username;
    private String password;
    private String passiveMode;
    private String encoding;
    private int clientTimeout;
    private int transferFileType;
}

2、配置文件

ftp.client.host=172.16.10.151
ftp.client.port=21
ftp.client.username=root
ftp.client.password=password123
ftp.client.encoding=utf-8
ftp.client.passiveMode=true
ftp.client.connectTimeout=30000
ftp.client.transferFileType=2

3、连接池

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
@Slf4j
public class FtpClientFactory implements PooledObjectFactory<FTPClient> {
    @Autowired
    private FtpPoolConfig config;

    @Override
    public void destroyObject(PooledObject<FTPClient> pool) throws Exception {
        FTPClient ftpClient = pool.getObject();
        if (ftpClient != null) {
            try {
                ftpClient.disconnect();
                log.debug("销毁ftp连接");
            } catch (Exception e) {
                log.error("销毁ftpClient异常,error:", e.getMessage());
            }
        }
    }

    @Override
    public PooledObject<FTPClient> makeObject() throws Exception {

        FTPClient ftpClient = new FTPClient();
        ftpClient.setConnectTimeout(config.getClientTimeout());
        ftpClient.connect(config.getHost(), config.getPort());
        int reply = ftpClient.getReplyCode();
        if (!FTPReply.isPositiveCompletion(reply)) {
            ftpClient.disconnect();
            return null;
        }
        boolean success = false;
        if (StringUtils.isBlank(config.getUsername())) {
            success = ftpClient.login("anonymous", "anonymous");
        } else {
            success = ftpClient.login(config.getUsername(), config.getPassword());
        }
        if (!success) {
            return null;
        }
        ftpClient.setFileType(config.getTransferFileType());
        ftpClient.setBufferSize(1024);
        ftpClient.setControlEncoding(config.getEncoding());
        if (config.getPassiveMode() != null && config.getPassiveMode().equals("true")) {
            ftpClient.enterLocalPassiveMode();
        }
        log.debug("创建ftp连接");
        return new DefaultPooledObject<>(ftpClient);
    }

    @Override
    public boolean validateObject(PooledObject<FTPClient> pool) {
        FTPClient ftpClient = pool.getObject();
        try {
            return ftpClient != null && ftpClient.sendNoOp();
        } catch (IOException e) {
            return false;
        }
    }

    @Override
    public void passivateObject(PooledObject<FTPClient> p) throws Exception {
    }

    @Override
    public void activateObject(PooledObject<FTPClient> pool) throws Exception {
    }
}

4、操作接口


import org.apache.commons.net.ftp.FTPClient;

public interface FtpPoolService {
    /**
     * 获取ftpClient
     */
    FTPClient borrowObject() throws Exception;

    /**
     * 归还ftpClient
     */
    void returnObject(FTPClient ftpClient) throws Exception;

}

5、操作实现类


import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;


@Component
public class FtpPoolServiceImpl implements FtpPoolService {
    private GenericObjectPool<FTPClient> pool;
    @Autowired
    private FtpPoolConfig config;
    @Autowired
    private FtpClientFactory factory;

    /**
     * 初始化pool
     */
    @PostConstruct
    private void initPool() {
        config.setTestOnBorrow(true);
        config.setTestOnReturn(true);
        this.pool = new GenericObjectPool<FTPClient>(this.factory, this.config);
    }

    /**
     * 获取ftpClient
     */
    @Override
    public FTPClient borrowObject() throws Exception {
        if (this.pool != null) {
            return this.pool.borrowObject();
        }
        return null;
    }

    /**
     * 归还 ftpClient
     */
    @Override
    public void returnObject(FTPClient ftpClient) {
        if (this.pool != null && ftpClient != null) {
            this.pool.returnObject(ftpClient);
        }
    }
}

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
经过几天的琢磨,去看了csdn上一位大牛的数据库的连接池实现方案,从中感悟很多,感谢这位大神。 让我才能有信心去坚持下去。也不知道写的好不好··不好的话,大家指出。但是我是努力去做了,这一个过程,很享受,大家互相学习吧~ 其实ftp连接池跟数据库连接池的原理是差不多的,不同的是ftp连接池有个连接时间的限制,如果你没设置的话,它的默认连接服务器的时间是0,所以我们要合理的设置它的服务器的时间,ftp.setConnectTimeout(5000);在这里设置了它的时间是5s。 写ftp连接池的目的就是合理的利用资源,本文的目的是在初始的时候,创建10个Ftp连接,放到一个队列中去,当多个用户同时去下载ftp上的文件的时候,就会从队列中取,若当前的队列中存在着空闲的连接,就获取该ftp的连接,并设置此连接为忙的状态,否则就在创建新的连接到连接池中去(有最大的连接池数的限制,不能超过这个连接数,超过的话,就会进入等待状态,直到其它连接释放连接),在执行下载操作的前对登录ftp时间进行判断。看是否超时,超时的话,就重新连接到ftp服务器,在这里我所做的操作就是,在开始创建ftp连接池的时候,记录下系统的当前时间,例如为:long beginTime=System.currentTimeMillis(),在取文件之前获得 当前系统的时间 long endTime=System.currentTimeMillis(),此时我们就可以获得系统登录ftp的时间time=endTime-beginTime,在此我们可以用time与ftp最大登录服务器时间(ftpPool.getConnection();)进行比较。 当然了,在操作完之后我们需要将所操作的连接池中的ftp设置为空闲状态。代码在文件中,为了测试,我本地自己创建了一个ftp服务器,创建ftp的方法,大家可以到网上查资料,我用的是Serv-U工具。傻瓜式的。所用到的jar包是commons-net2.0.
以下是使用SpringBoot实现FTP连接池的步骤: 1.在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.6</version> </dependency> ``` 2.创建FTP连接池配置类FtpClientPoolConfig,配置连接池的最大、最小连接数、连接超时时间等参数。 ```java @Configuration @ConfigurationProperties(prefix = "ftp.pool") @Data public class FtpClientPoolConfig { private int maxTotal; private int maxIdle; private int minIdle; private long maxWaitMillis; private boolean testOnBorrow; private boolean testOnReturn; private boolean testWhileIdle; private long timeBetweenEvictionRunsMillis; private int numTestsPerEvictionRun; private long minEvictableIdleTimeMillis; } ``` 3.创建FTP连接池FtpClientPool,使用Apache Commons Pool2实现连接池。 ```java @Component public class FtpClientPool extends GenericObjectPool<FtpClient> { public FtpClientPool(FtpClientFactory factory, FtpClientPoolConfig config) { super(factory, new GenericObjectPoolConfig()); this.setMaxTotal(config.getMaxTotal()); this.setMaxIdle(config.getMaxIdle()); this.setMinIdle(config.getMinIdle()); this.setMaxWaitMillis(config.getMaxWaitMillis()); this.setTestOnBorrow(config.isTestOnBorrow()); this.setTestOnReturn(config.isTestOnReturn()); this.setTestWhileIdle(config.isTestWhileIdle()); this.setTimeBetweenEvictionRunsMillis(config.getTimeBetweenEvictionRunsMillis()); this.setNumTestsPerEvictionRun(config.getNumTestsPerEvictionRun()); this.setMinEvictableIdleTimeMillis(config.getMinEvictableIdleTimeMillis()); } } ``` 4.创建FTP连接池工厂类FtpClientFactory,用于创建FTP连接对象。 ```java @Component public class FtpClientFactory extends BasePooledObjectFactory<FtpClient> { private FtpClientProperties properties; public FtpClientFactory(FtpClientProperties properties) { this.properties = properties; } @Override public FtpClient create() throws Exception { FtpClient ftpClient = new FtpClient(); ftpClient.connect(properties.getHost(), properties.getPort()); ftpClient.login(properties.getUsername(), properties.getPassword()); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); ftpClient.setBufferSize(properties.getBufferSize()); ftpClient.setControlEncoding(properties.getEncoding()); ftpClient.enterLocalPassiveMode(); return ftpClient; } @Override public PooledObject<FtpClient> wrap(FtpClient ftpClient) { return new DefaultPooledObject<>(ftpClient); } @Override public void destroyObject(PooledObject<FtpClient> p) throws Exception { FtpClient ftpClient = p.getObject(); if (ftpClient.isConnected()) { ftpClient.logout(); ftpClient.disconnect(); } } @Override public boolean validateObject(PooledObject<FtpClient> p) { FtpClient ftpClient = p.getObject(); try { return ftpClient.sendNoOp(); } catch (IOException e) { return false; } } } ``` 5.创建FTP连接池属性类FtpClientProperties,用于配置FTP连接的相关参数。 ```java @ConfigurationProperties(prefix = "ftp") @Data public class FtpClientProperties { private String host; private int port; private String username; private String password; private int bufferSize; private String encoding; } ``` 6.在application.yml文件中配置FTP连接池的相关参数。 ```yaml ftp: host: ftp.example.com port: 21 username: username password: password bufferSize: 1024 encoding: UTF-8 ftp: pool: maxTotal: 10 maxIdle: 5 minIdle: 1 maxWaitMillis: 3000 testOnBorrow: true testOnReturn: false testWhileIdle: true timeBetweenEvictionRunsMillis: 60000 numTestsPerEvictionRun: -1 minEvictableIdleTimeMillis: 1800000 ``` 7.在需要使用FTP连接的地方,注入FtpClientPool对象,从连接池中获取FTP连接对象。 ```java @Service public class FtpService { @Autowired private FtpClientPool ftpClientPool; public void uploadFile(String remotePath, String fileName, InputStream inputStream) throws Exception { FtpClient ftpClient = ftpClientPool.borrowObject(); try { ftpClient.changeWorkingDirectory(remotePath); ftpClient.storeFile(fileName, inputStream); } finally { ftpClientPool.returnObject(ftpClient); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_lrs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值