Java FTPClient连接池的实现

最近在写一个FTP上传工具,用到了Apache的FTPClient,为了提高上传效率,我采用了多线程的方式,但是每个线程频繁的创建和销毁FTPClient对象势必会造成不必要的开销,因此,此处最好使用一个FTPClient连接池。仔细翻了一下Apache的api,发现它并没有一个FTPClientPool的实现,所以,不得不自己写一个FTPClientPool。下面就大体介绍一下开发连接池的整个过程,供大家参考。

关于对象池

有些对象的创建开销是比较大的,比如数据库连接等。为了减少频繁创建、销毁对象带来的性能消耗,我们可以利用对象池的技术来实现对象的复用。对象池提供了一种机制,它可以管理对象池中对象的生命周期,提供了获取和释放对象的方法,可以让客户端很方便的使用对象池中的对象。

如果我们要自己实现一个对象池,一般需要完成如下功能:

1. 如果池中有可用的对象,对象池应当能返回给客户端
2. 客户端把对象放回池里后,可以对这些对象进行重用
3. 对象池能够创建新的对象来满足客户端不断增长的需求
4. 需要有一个正确关闭池的机制来结束对象的生命周期

Apache的对象池工具包

为了方便我们开发自己的对象池,Apache 提供的common-pool工具包,里面包含了开发通用对象池的一些接口和实现类。其中最基本的两个接口是ObjectPool 和PoolableObjectFactory。

ObjectPool接口中有几个最基本的方法:

1. addObject() : 添加对象到池
2. borrowObject():客户端从池中借出一个对象
3. returnObject():客户端归还一个对象到池中
4. close():关闭对象池,清理内存释放资源等
5. setFactory(ObjectFactory factory):需要一个工厂来制造池中的对象

PoolableObjectFactory接口中几个最基本的方法:

1. makeObject():制造一个对象
2. destoryObject():销毁一个对象
3. validateObject():验证一个对象是否还可用

通过以上两个接口我们就可以自己实现一个对象池了。

实例:开发一个FTPClient对象池

最近在开发一个项目,需要把hdfs中的文件上传到一组ftp服务器,为了提高上传效率,自然考虑到使用多线程的方式进行上传。我上传ftp用的工具是Apache common-net包中的FTPClient,但Apache并没有提供FTPClientPool,于是为了减少FTPClient的创建销毁次数,我们就自己开发一个FTPClientPool来复用FTPClient连接。

通过上面的介绍,我们可以利用Apache提供的common-pool包来协助我们开发连接池。而开发一个简单的对象池,仅需要实现common-pool 包中的ObjectPool和PoolableObjectFactory两个接口即可。下面就看一下我写的实现:

写一个ObjectPool接口的实现FTPClientPool

import java.io.IOException;
import java.util.NoSuchElementException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.PoolableObjectFactory;

/**
* 实现了一个FTPClient连接池
* @author heaven
*/
public class FTPClientPool implements ObjectPool<FTPClient>{
 private static final int DEFAULT_POOL_SIZE = 10;
 private final BlockingQueue<FTPClient> pool;
 private final FtpClientFactory factory;

 /**
 * 初始化连接池,需要注入一个工厂来提供FTPClient实例
 * @param factory
 * @throws Exception
 */
 public FTPClientPool(FtpClientFactory factory) throws Exception{
   this(DEFAULT_POOL_SIZE, factory);
 }
 /**
 *
 * @param maxPoolSize
 * @param factory
 * @throws Exception
 */
 public FTPClientPool(int poolSize, FtpClientFactory factory) throws Exception {
   this.factory = factory;
   pool = new ArrayBlockingQueue<FTPClient>(poolSize*2);
   initPool(poolSize);
 }
 /**
 * 初始化连接池,需要注入一个工厂来提供FTPClient实例
 * @param maxPoolSize
 * @throws Exception
 */
 private void initPool(int maxPoolSize) throws Exception {
   for(int i=0;i<maxPoolSize;i++){
      //往池中添加对象
      addObject();
   }

 }
 /* (non-Javadoc)
 * @see org.apache.commons.pool.ObjectPool#borrowObject()
 */
 public FTPClient borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
   FTPClient client = pool.take();
   if (client == null) {
      client = factory.makeObject();
      addObject();
   }else if(!factory.validateObject(client)){//验证不通过
      //使对象在池中失效
      invalidateObject(client);
      //制造并添加新对象到池中
      client = factory.makeObject();
      addObject();
   }
   return client;

 }

 /* (non-Javadoc)
 * @see org.apache.commons.pool.ObjectPool#returnObject(java.lang.Object)
 */
 public void returnObject(FTPClient client) throws Exception {
   if ((client != null) && !pool.offer(client,3,TimeUnit.SECONDS)) {
      try {
        factory.destroyObject(client);
      } catch (IOException e) {
        e.printStackTrace();
      }
   }
 }

 public void invalidateObject(FTPClient client) throws Exception {
   //移除无效的客户端
   pool.remove(client);
 }

 /* (non-Javadoc)
 * @see org.apache.commons.pool.ObjectPool#addObject()
 */
 public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
   //插入对象到队列
   pool.offer(factory.makeObject(),3,TimeUnit.SECONDS);
 }

 public int getNumIdle() throws UnsupportedOperationException {
   return 0;
 }

 public int getNumActive() throws UnsupportedOperationException {
   return 0;
 }

 public void clear() throws Exception, UnsupportedOperationException {

 }

 /* (non-Javadoc)
 * @see org.apache.commons.pool.ObjectPool#close()
 */
 public void close() throws Exception {
   while(pool.iterator().hasNext()){
      FTPClient client = pool.take();
      factory.destroyObject(client);
   }
 }

 public void setFactory(PoolableObjectFactory<FTPClient> factory) throws IllegalStateException, UnsupportedOperationException {

 }
}

再写一个PoolableObjectFactory接口的实现FTPClientFactory

import java.io.IOException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.pool.PoolableObjectFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hdfstoftp.util.FTPClientException;

/**
* FTPClient工厂类,通过FTPClient工厂提供FTPClient实例的创建和销毁
* @author heaven
*/
public class FtpClientFactory implements PoolableObjectFactory<FTPClient> {
private static Logger logger = LoggerFactory.getLogger("file");
 private FTPClientConfigure config;
 //给工厂传入一个参数对象,方便配置FTPClient的相关参数
 public FtpClientFactory(FTPClientConfigure config){
   this.config=config;
 }

 /* (non-Javadoc)
 * @see org.apache.commons.pool.PoolableObjectFactory#makeObject()
 */
 public FTPClient makeObject() throws Exception {
   FTPClient ftpClient = new FTPClient();
   ftpClient.setConnectTimeout(config.getClientTimeout());
   try {
      ftpClient.connect(config.getHost(), config.getPort());
      int reply = ftpClient.getReplyCode();
      if (!FTPReply.isPositiveCompletion(reply)) {
        ftpClient.disconnect();
        logger.warn("FTPServer refused connection");
        return null;
      }
      boolean result = ftpClient.login(config.getUsername(), config.getPassword());
      if (!result) {
        throw new FTPClientException("ftpClient登陆失败! userName:" + config.getUsername() + " ; password:" + config.getPassword());
      }
      ftpClient.setFileType(config.getTransferFileType());
      ftpClient.setBufferSize(1024);
      ftpClient.setControlEncoding(config.getEncoding());
      if (config.getPassiveMode().equals("true")) {
        ftpClient.enterLocalPassiveMode();
      }
   } catch (IOException e) {
      e.printStackTrace();
   } catch (FTPClientException e) {
      e.printStackTrace();
   }
   return ftpClient;
 }

 /* (non-Javadoc)
 * @see org.apache.commons.pool.PoolableObjectFactory#destroyObject(java.lang.Object)
 */
 public void destroyObject(FTPClient ftpClient) throws Exception {
   try {
      if (ftpClient != null && ftpClient.isConnected()) {
        ftpClient.logout();
      }
   } catch (IOException io) {
      io.printStackTrace();
   } finally {
      // 注意,一定要在finally代码中断开连接,否则会导致占用ftp连接情况
      try {
        ftpClient.disconnect();
      } catch (IOException io) {
        io.printStackTrace();
      }
   }
 }

 /* (non-Javadoc)
 * @see org.apache.commons.pool.PoolableObjectFactory#validateObject(java.lang.Object)
 */
 public boolean validateObject(FTPClient ftpClient) {
   try {
      return ftpClient.sendNoOp();
   } catch (IOException e) {
      throw new RuntimeException("Failed to validate client: " + e, e);
   }
 }

 public void activateObject(FTPClient ftpClient) throws Exception {
 }

 public void passivateObject(FTPClient ftpClient) throws Exception {

 }
}

最后,我们最好给工厂传递一个参数对象,方便我们设置FTPClient的一些参数

package org.apache.commons.pool.impl.contrib;

/**
 * FTPClient配置类,封装了FTPClient的相关配置
 *
 * @author heaven
 */
public class FTPClientConfigure {
 private String host;
 private int port;
 private String username;
 private String password;
 private String passiveMode;
 private String encoding;
 private int clientTimeout;
 private int threadNum;
 private int transferFileType;
 private boolean renameUploaded;
 private int retryTimes;

 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 getPassiveMode() {
     return passiveMode;
 }

 public void setPassiveMode(String passiveMode) {
     this. passiveMode = passiveMode;
 }

 public String getEncoding() {
     return encoding;
 }

 public void setEncoding(String encoding) {
     this. encoding = encoding;
 }

 public int getClientTimeout() {
     return clientTimeout;
 }

 public void setClientTimeout( int clientTimeout) {
     this. clientTimeout = clientTimeout;
 }

 public int getThreadNum() {
     return threadNum;
 }

 public void setThreadNum( int threadNum) {
     this. threadNum = threadNum;
 }

 public int getTransferFileType() {
     return transferFileType;
 }

 public void setTransferFileType( int transferFileType) {
     this. transferFileType = transferFileType;
 }

 public boolean isRenameUploaded() {
     return renameUploaded;
 }

 public void setRenameUploaded( boolean renameUploaded) {
     this. renameUploaded = renameUploaded;
 }

 public int getRetryTimes() {
     return retryTimes;
 }

 public void setRetryTimes( int retryTimes) {
     this. retryTimes = retryTimes;
 }

 @Override
 public String toString() {
     return "FTPClientConfig [host=" + host + "\n port=" + port + "\n username=" + username + "\n password=" + password  + "\n passiveMode=" + passiveMode
          + "\n encoding=" + encoding + "\n clientTimeout=" + clientTimeout + "\n threadNum=" + threadNum + "\n transferFileType="
          + transferFileType + "\n renameUploaded=" + renameUploaded + "\n retryTimes=" + retryTimes + "]" ;
 }

}

FTPClientPool连接池类管理FTPClient对象的生命周期,负责对象的借出、规划、池的销毁等;FTPClientPool类依赖于FtpClientFactory类,由这个工程类来制造和销毁对象;FtpClientFactory又依赖FTPClientConfigure类,FTPClientConfigure负责封装FTPClient的配置参数。至此,我们的FTPClient连接池就开发完成了。

需要注意的是,FTPClientPool中用到了一个阻塞队列ArrayBlockingQueue来管理存放FTPClient对象,关于阻塞队列,请参考我的这篇文章: 【Java并发之】BlockingQueue

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java FTPClient实现文件上传下载

    在JAVA程序中,经常需要和FTP打交道,比如向FTP服务器上传文件.下载文件,本文简单介绍如何利用jakarta commons中的FTPClient(在commons-net包中)实现上传下载文件. 所用到的jar包有:  commons-net-1.4.1.jar  jakarta-oro.jar  一.上传文件 文件上传源代码 /** * Description: 向FTP服务器上传文件 * @Version1.0 * @param url FTP服务器hostname * @param

  • 详解JAVA中使用FTPClient工具类上传下载

    详解JAVA中使用FTPClient工具类上传下载 在Java程序中,经常需要和FTP打交道,比如向FTP服务器上传文件.下载文件.本文简单介绍如何利用jakarta commons中的FTPClient(在commons-net包中)实现上传下载文件. 1.写一个javabean文件,描述ftp上传或下载的信息 实例代码: public class FtpUseBean { private String host; private Integer port; private String us

  • JAVA中使用FTPClient实现文件上传下载实例代码

    在java程序开发中,ftp用的比较多,经常打交道,比如说向FTP服务器上传文件.下载文件,本文给大家介绍如何利用jakarta commons中的FTPClient(在commons-net包中)实现上传下载文件. 一.上传文件 原理就不介绍了,大家直接看代码吧 /** * Description: 向FTP服务器上传文件 * @Version1.0 Jul 27, 2008 4:31:09 PM by 崔红保(cuihongbao@d-heaven.com)创建 * @param url F

  • Java使用FTPClient类读写FTP

    本文实例为大家分享了Java使用FTPClient类读写FTP的具体代码,供大家参考,具体内容如下 1.首先先导入相关jar包 2.创建一个连接FTP的工具类FTPUtil.java package com.metarnet.ftp.util; import java.io.IOException; import java.io.InputStream; import java.net.SocketException; import java.util.Properties; import or

  • java web FTPClient实现上传文件到指定服务器

    FPClient 实现上传文件到指定服务器,供大家参考,具体内容如下 调用 FileInputStream in=new FileInputStream(new File(fileUrl)); moveFile("10.3.3.**", 21, "username", "password", path, filename, in); 方法 /** * Description: 向FTP服务器上传文件 * @param url FTP服务器host

  • Java中FTPClient上传中文目录、中文文件名乱码问题解决方法

    问题描述: 使用org.apache.commons.net.ftp.FTPClient创建中文目录.上传中文文件名时,目录名及文件名中的中文显示为"??". 原因: FTP协议里面,规定文件名编码为iso-8859-1,所以目录名或文件名需要转码. 解决方案: 1.将中文的目录或文件名转为iso-8859-1编码的字符.参考代码: 复制代码 代码如下: String name="目录名或文件名"; name=new String(name.getBytes(&qu

  • Java FtpClient 实现文件上传服务

    一.Ubuntu 安装 Vsftpd 服务 1.安装 sudo apt-get install vsftpd 2.添加用户(uftp) sudo useradd -d /home/uftp -s /bin/bash uftp 3.设置用户密码 sudo useradd -d /home/uftp -s /bin/bash uftp 4.创建用户目录 sudo mkdir /home/uftp 5.设置用户密码 sudo passwd uftp 6.设置/etc/vsftpd.conf配置文件 s

  • Java FTPClient连接池的实现

    最近在写一个FTP上传工具,用到了Apache的FTPClient,为了提高上传效率,我采用了多线程的方式,但是每个线程频繁的创建和销毁FTPClient对象势必会造成不必要的开销,因此,此处最好使用一个FTPClient连接池.仔细翻了一下Apache的api,发现它并没有一个FTPClientPool的实现,所以,不得不自己写一个FTPClientPool.下面就大体介绍一下开发连接池的整个过程,供大家参考. 关于对象池 有些对象的创建开销是比较大的,比如数据库连接等.为了减少频繁创建.销毁

  • Java httpClient连接池支持多线程高并发的实现

    当采用HttpClient httpClient = HttpClients.createDefault() 实例化的时候.会导致Address already in use的异常. 信息: I/O exception (java.net.BindException) caught when processing request to {}->http://**.**.**.** Address already in use: connect 十一月 22, 2018 5:02:13 下午 or

  • Java Druid连接池与Apache的DBUtils使用教程

    目录 Druid连接池 连接池思想 Druid连接池使用步骤 引入相关jar包 创建database.properties配置文件 编写连接池工具类 Druid连接池测试 Apache的DBUtils使用 Apache DBUtils介绍 Apache DBUtils特征 Apache DbUtils主要组成 Apache DbUtils使用步骤 综合案例 创建product表 向表中添加数据 创建实体类Product 创建ProductDao接口 创建ProductDaoImpl实现类 创建P

  • 如何解决线程太多导致java socket连接池出现的问题

    这篇文章主要介绍了如何解决线程太多导致socket连接池出现的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 线程太多导致socket连接池爆满,进程启动不了 问题: 某部机上跟其它机器的连接有问题,ping可以通,telnet端口不通,可以其它机器可以连接到该机器上的进程. java应用启动不起来,产生以下错误. java.net.SocketException: No buffer space available (maximum co

  • Java dbcp连接池基本使用方法详解

    1.依赖api的使用 导入jar包 <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.7.0</version&g

  • Java中Druid连接池连接超时获取不到连接的解决

    错误内容: com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 30000, active 600, maxActive 600, creating 0 detail: Service Error:Cannot find a proper coonection from STDB 错误日志截图: 解决过程: 1.添加了三个参数 作用是如果超过3分钟,连接未释放,那么关闭连接,并报错. 2.进行请求,并查看日志 确认获

  • 关于Java 项目封装sqlite连接池操作持久化数据的方法

    Sqlite sqlite是C实现的一个开源SQL引擎,其api提供sql语法支持,通过sql解析后对存储层的磁盘文件进行操作,完整配置的sqlite库小于400kb,多用于移动端应用,小型项目中. 对Sqlite有兴趣的可以了解下其体系结构 之前自研SQL解析器的时候便是借鉴了SQLcompiler的源码,这里不展开介绍 封装Java的Sqlite连接池 首先maven项目引入依赖sqlite-jdbc,其主要是java版的sqliteapi,关于Sqlite api的操作,大家可以看菜鸟教程

  • Java实现数据连接池Druid举例

    目录 开篇 Druid的调试 参考 开篇 Druid号称是Java语言中最好的数据库连接池,并且能够提供强大的监控和扩展功能.作为日常使用较多的数据库连接组件,纯粹个人兴趣研究下理解下的实现原理. 理解一个工具组件最好的方式就是进行 debug,这里建议大家下载下参考连接中的 druid demo,修改下具体的数据库连接参数就可以直接进行调试跟踪. 之所以强调 Demo 的重要性,在于通过 demo 能够跟踪所有的执行流程,有了 Demo 剩下的事情只要花时间都能很好的梳理. Druid的调试

  • Spring Boot整合FTPClient线程池的实现示例

    最近在写一个FTP上传工具,用到了Apache的FTPClient,但是每个线程频繁的创建和销毁FTPClient对象对服务器的压力很大,因此,此处最好使用一个FTPClient连接池.仔细翻了一下Apache的api,发现它并没有一个FTPClientPool的实现,所以,不得不自己写一个FTPClientPool.下面就大体介绍一下开发连接池的整个过程,供大家参考. 我们可以利用Apache提供的common-pool包来协助我们开发连接池.而开发一个简单的对象池,仅需要实现common-p

  • java配置dbcp连接池(数据库连接池)示例分享

    使用jar包:commons-dbcp-1.4.jar.commons-pool-1.6.jar.commons-logging-1.1.3.jar,另外还有数据库的jdbc驱动,适用java6及以上平台 连接池管理类 复制代码 代码如下: package cn.songxinqiang.samples.commonsdbcp.util; import java.sql.Connection;import java.sql.DatabaseMetaData;import java.sql.SQL

随机推荐