栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

common-pool2实现sftp连接池

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

common-pool2实现sftp连接池

背景

项目使用sftp作为存储介质,需要大量的上传下载,单例的sftp不能满足场景,使用ThreadLocal存储sftp连接,会频繁创建销毁连接,性能太差,于是想到将连接对象池化。

连接池介绍

对象池顾名思义就是存放对象的池,与我们常听到的线程池、数据库连接池、http连接池等一样,都是典型的池化设计思想。

对象池的优点就是可以集中管理池中对象,减少频繁创建和销毁长期使用的对象,从而提升复用性,以节约资源的消耗,可以有效避免频繁为对象分配内存和释放堆中内存,进而减轻jvm垃圾收集器的负担,避免内存抖动。

Apache Common Pool2 是Apache提供的一个通用对象池技术实现,可以方便定制化自己需要的对象池,大名鼎鼎的 Redis 客户端 Jedis 内部连接池就是基于它来实现的。

  • 依赖
   
        com.jcraft
        jsch
        0.1.55
    
    
        org.apache.commons
        commons-pool2
        2.8.0
    
  • sftpClient,用于创建连接,验证连接,销毁连接使用
package com.zhanghao.base.util;

import com.jcraft.jsch.*;
import lombok.Data;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicLong;


@Data
public class SftpClient {
  private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  private static final AtomicLong CLIENT_NUMBER = new AtomicLong(1L);
  private ChannelSftp channelSftp;
  private Session session;
  
  private String clientInfo = "sftpclient";
  
  private String originalDir;
  //创建连接
  public SftpClient(SftpProperties sftpProperties) throws SftpException, JSchException {
    try {
      JSch jsch = new JSch();
      session = jsch.getSession(sftpProperties.getUsername(), sftpProperties.getHost(), sftpProperties.getPort());
      session.setPassword(sftpProperties.getPassword());
      Properties config = new Properties();
      if (sftpProperties.getSession() != null) {
        sftpProperties.getSession().forEach(config::put);
      }
      session.setConfig(config);
      session.connect();
      channelSftp = (ChannelSftp) session.openChannel("sftp");
      channelSftp.connect();
      clientInfo += CLIENT_NUMBER.getAndIncrement() + ",createTime:" + DATE_TIME_FORMATTER.format(LocalDateTime.now());
      originalDir = channelSftp.pwd();
    } catch (Exception e) {
      disconnect();
      throw e;
    }
  }
 //断开连接
  public void disconnect() {
    if (channelSftp != null) {
      try {
        channelSftp.disconnect();
      } catch (Exception ignored) {
      }
    }
    if (session != null) {
      try {
        session.disconnect();
      } catch (Exception ignored) {
      }
    }
  }
 //验证连接
  public boolean validateConnect() {
    try {
      return session.isConnected() && channelSftp.isConnected() && originalDir.equals(channelSftp.pwd());
    } catch (Exception e) {
      return false;
    }
  }
}


  • 配置
package com.zhanghao.base.util;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.Map;


@Component
@ConfigurationProperties("sftp")
@Getter
@Setter
public class SftpProperties {
  
  private String host;
  
  private int port = 22;
  
  private String usernamel;
  
  private String password;
  
  private Map session;
  
  private Pool pool;


  
  @Data
  public static class Pool {
    
    private int minIdle = 0;

    
    private int maxIdle = 8;

    
    private int maxActive = 16;

    
    private long maxWait = -1;

    
    private boolean testOnBorrow = true;

    
    private boolean testOnReturn = false;

    
    private boolean testWhileIdle = true;

    
    private long timeBetweenEvictionRuns = 300000L;
  }
}


  • 连接池(核心)
package com.zhanghao.base.util;

import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;



public class SftpPool implements ObjectPool {
  private final GenericObjectPool internalPool;

  public SftpPool(SftpProperties sftpProperties) {
    this.internalPool = new GenericObjectPool(new SftpFactory(sftpProperties), getPoolConfig(sftpProperties.getPool())){
      @Override
      public void returnObject(SftpClient sftpClient) {
        try {
          sftpClient.getChannelSftp().cd(sftpClient.getOriginalDir());
        } catch (Exception ignored) {
        }
        super.returnObject(sftpClient);
      }
    };
  }

  @Override
  public void addObject() throws Exception {
    internalPool.addObject();
  }

  @Override
  public SftpClient borrowObject() throws Exception {
    return internalPool.borrowObject();
  }

  @Override
  public void clear() {
    internalPool.clear();
  }

  @Override
  public void close() {
    internalPool.close();
  }

  @Override
  public int getNumActive() {
    return internalPool.getNumActive();
  }

  @Override
  public int getNumIdle() {
    return internalPool.getNumIdle();
  }

  @Override
  public void invalidateObject(SftpClient obj) throws Exception {
    internalPool.invalidateObject(obj);
  }

  @Override
  public void returnObject(SftpClient obj) {
    internalPool.returnObject(obj);
  }

  private static class SftpFactory extends BasePooledObjectFactory {

    private final SftpProperties sftpProperties;

    public SftpFactory(SftpProperties sftpProperties) {
      this.sftpProperties = sftpProperties;
    }

    @Override
    public SftpClient create() throws Exception {
      return new SftpClient(sftpProperties);
    }

    @Override
    public PooledObject wrap(SftpClient sftpClient) {
      return new DefaultPooledObject<>(sftpClient);
    }

    @Override
    public boolean validateObject(PooledObject p) {
      return p.getObject().validateConnect();
    }

    @Override
    public void destroyObject(PooledObject p) {
      p.getObject().disconnect();
    }

  }

  private GenericObjectPoolConfig getPoolConfig(SftpProperties.Pool properties) {
    if (properties == null) {
      properties = new SftpProperties.Pool();
    }
    GenericObjectPoolConfig config = new GenericObjectPoolConfig<>();
    config.setMinIdle(properties.getMinIdle());
    config.setMaxIdle(properties.getMaxIdle());
    config.setMaxTotal(properties.getMaxActive());
    config.setMaxWaitMillis(properties.getMaxWait());
    config.setTestOnBorrow(properties.isTestOnBorrow());
    config.setTestOnReturn(properties.isTestOnReturn());
    config.setTestWhileIdle(properties.isTestWhileIdle());
    config.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRuns());
    return config;
  }
}


  • 实例化对象池
package com.zhanghao.base.util;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class SftpPoolConfig {

    @Bean
    public SftpPool getSftpPool(SftpProperties sftpProperties){
        return new SftpPool(sftpProperties);
    }
}

  • demo
@Resource
    private SftpPool sftpPool;
    
    @GetMapping("/file/download")
    public Integer fileDownload() {
            SftpClient sftpClient = null;
                try {
                    log.info("sftp连接池大小{}",sftpPool.getNumIdle());
                    sftpClient = sftpPool.borrowObject();
                    InputStream inputStream = sftpClient.getChannelSftp().get("/upload/233.jpg");
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    IOUtils.copy(inputStream,byteArrayOutputStream);
                    return byteArrayOutputStream.size();
                } catch (Exception e1) {
                    log.info("下载异常!",e1);
                    try {
                        sftpPool.invalidateObject(sftpClient);
                    } catch (Exception e2) {
                        log.info("销毁sftp连接异常!",e2);
                    }
                    sftpClient = null;
                } finally {
                    if (null != sftpClient) {
                        sftpPool.returnObject(sftpClient);
                    }
                }
            return null;
        }

  • 压测数据解析

2022-07-26 18:26:06.467  INFO 6424 --- [io-8090-exec-77] c.zhanghao.base.biz.BizCommonController  : sftp连接池大小8
2022-07-26 18:26:06.471  INFO 6424 --- [o-8090-exec-113] c.zhanghao.base.biz.BizCommonController  : sftp连接池大小7

总结:

根据上文的配置,200线程下,最大创建了8个连接对象,根据根据自己的需要调整最大连接对象数和空闲数,正常情况下保持几个连接就够用了。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1027834.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号