Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类 ...

打印 上一主题 下一主题

主题 925|帖子 925|积分 2775

Java BasePooledObjectFactory 对象池化技术
通常一个对象创建、销毁非常耗时的时候,我们不会频繁的创建和销毁它,而是考虑复用。复用对象的一种做法就是对象池,将创建好的对象放入池中维护起来,下次再用的时候直接拿池中已经创建好的对象继续用,这就是池化的思想。
Apache Commons Pool是一个对象池的框架,他提供了一整套用于实现对象池化的API。它提供了三种对象池:GenericKeyedObjectPool,SoftReferenceObjectPool和GenericObjectPool,其中GenericObjectPool是我们最常用的对象池,内部实现也最复杂。
GenericObjectPool

GenericObjectPool 是一个通用对象池框架,我们可以借助它实现一个健壮的对象池,UML图如下所示:

GenericObjectPool 实现了ObjectPool接口,而ObjectPool就是对象池的核心接口,它定义了一个对象池应该实现的行为。
  1. public interface ObjectPool<T> extends Closeable {
  2.     /**
  3.      * 从池中借走到一个对象
  4.      */
  5.     T borrowObject() throws Exception, NoSuchElementException, IllegalStateException;
  6.     /**
  7.      * 把对象归还给对象池
  8.      */
  9.     void returnObject(T var1) throws Exception;
  10.     /**
  11.      * 验证对象的有效性
  12.      */
  13.     void invalidateObject(T var1) throws Exception;
  14.     /**
  15.      * 往池中添加一个对象
  16.      */
  17.     void addObject() throws Exception, IllegalStateException, UnsupportedOperationException;
  18.     /**
  19.      * 返回对象池中有多少对象是空闲的,也就是能够被借走的对象的数量。
  20.      */
  21.     int getNumIdle();
  22.     /**
  23.      * 返回对象池中有对象对象是活跃的,也就是已经被借走的,在使用中的对象的数量。
  24.      */
  25.     int getNumActive();
  26.     /**
  27.      * 清理对象池。注意是清理不是清空,该方法要求的是,清理所有空闲对象,释放相关资源。
  28.      */
  29.     void clear() throws Exception, UnsupportedOperationException;
  30.     /**
  31.      * 关闭对象池。这个方法可以达到清空的效果,清理所有对象以及相关资源。
  32.      */
  33.     void close();
  34. }
复制代码
BasePooledObjectFactory

Java BasePooledObjectFactory 对象池化技术
使用GenericObjectPool只需要创建一个对象工厂类,继承BasePooledObjectFactory并重写它的create()和destroyObject()。
如下文中的:SftpPool.java
  1. public interface PooledObjectFactory<T> {
  2.     /**
  3.      * 创建一个可由池提供服务的实例,并将其封装在由池管理的PooledObject中。
  4.      */
  5.     PooledObject<T> makeObject() throws Exception;
  6.     /**
  7.      *  销毁池不再需要的实例
  8.      */
  9.     void destroyObject(PooledObject<T> var1) throws Exception;
  10.     /**
  11.      * 确保实例可以安全地由池返回
  12.      */
  13.     boolean validateObject(PooledObject<T> var1);
  14.     /**
  15.      * 重新初始化池返回的实例
  16.      */
  17.     void activateObject(PooledObject<T> var1) throws Exception;
  18.     /**
  19.      * 取消初始化要返回到空闲对象池的实例
  20.      */
  21.     void passivateObject(PooledObject<T> var1) throws Exception;
  22. }
复制代码
配置类GenericObjectPoolConfig

GenericObjectPoolConfig是封装GenericObject池配置的简单“结构”,此类不是线程安全的;它仅用于提供创建池时使用的属性。大多数情况,可以使用GenericObjectPoolConfig提供的默认参数就可以满足日常的需求。
工作原理流程


  • 构造方法
    当我们执行构造方法时,主要工作就是创建了一个存储对象的LinkedList类型容器,也就是概念意义上的“池”
  • 从对象池中获取对象
    获取池中的对象是通过borrowObject()命令,源码比较复杂,简单而言就是去LinkedList中获取一个对象,如果不存在的话,要调用构造方法中第一个参数Factory工厂类的makeObject()方法去创建一个对象再获取,获取到对象后要调用validateObject方法判断该对象是否是可用的,如果是可用的才拿去使用。LinkedList容器减一
  • 归还对象到线程池
    简单而言就是先调用validateObject方法判断该对象是否是可用的,如果可用则归还到池中,LinkedList容器加一,如果是不可以的则调用destroyObject方法进行销毁
上面三步就是最简单的流程,由于取和还的流程步骤都在borrowObject和returnObject方法中固定的,所以我们只要重写Factory工厂类的makeObject()和validateObject以及destroyObject方法即可实现最简单的池的管理控制,通过构造方法传入该Factory工厂类对象则可以创建最简单的对象池管理类。这算是比较好的解耦设计模式,借和还的流程如下图所示:

使用Demo
  1. <dependency>
  2.     <groupId>org.apache.commons</groupId>
  3.     <artifactId>commons-pool2</artifactId>
  4.     <version>2.7.0</version>
  5. </dependency>
  6. <dependency>
  7.     <groupId>com.jcraft</groupId>
  8.     <artifactId>jsch</artifactId>
  9.     <version>0.1.55</version>
  10. </dependency>
复制代码
点击查看代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5.     <parent>
  6.         <artifactId>vipsoft-parent</artifactId>
  7.         <groupId>com.vipsoft.boot</groupId>
  8.         <version>1.0-SNAPSHOT</version>
  9.     </parent>
  10.     <modelVersion>4.0.0</modelVersion>
  11.     <artifactId>vipsoft-sftp</artifactId>
  12.     <version>1.0-SNAPSHOT</version>
  13.     <dependencies>
  14.         <dependency>
  15.             <groupId>org.apache.commons</groupId>
  16.             <artifactId>commons-pool2</artifactId>
  17.             <version>2.7.0</version>
  18.         </dependency>
  19.         
  20.         <dependency>
  21.             <groupId>com.jcraft</groupId>
  22.             <artifactId>jsch</artifactId>
  23.             <version>0.1.55</version>
  24.         </dependency>
  25.         <dependency>
  26.             <groupId>org.eclipse.paho</groupId>
  27.             <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
  28.             <version>1.2.5</version>
  29.         </dependency>
  30.         <dependency>
  31.             <groupId>cn.hutool</groupId>
  32.             <artifactId>hutool-all</artifactId>
  33.             <version>5.3.6</version>
  34.         </dependency>
  35.         <dependency>
  36.             <groupId>org.springframework.boot</groupId>
  37.             <artifactId>spring-boot-starter-web</artifactId>
  38.         </dependency>
  39.         <dependency>
  40.             <groupId>org.springframework.boot</groupId>
  41.             <artifactId>spring-boot-starter-actuator</artifactId>
  42.         </dependency>
  43.         <dependency>
  44.             <groupId>org.springframework.boot</groupId>
  45.             <artifactId>spring-boot-starter-test</artifactId>
  46.             <scope>test</scope>
  47.             <exclusions>
  48.                 <exclusion>
  49.                     <groupId>org.junit.vintage</groupId>
  50.                     <artifactId>junit-vintage-engine</artifactId>
  51.                 </exclusion>
  52.             </exclusions>
  53.         </dependency>
  54.     </dependencies>
  55.     <build>
  56.         <plugins>
  57.             <plugin>
  58.                 <groupId>org.springframework.boot</groupId>
  59.                 <artifactId>spring-boot-maven-plugin</artifactId>
  60.             </plugin>
  61.         </plugins>
  62.     </build>
  63. </project>
复制代码
application.yaml
  1. server:
  2.   port: 8088
  3.   application:
  4.     name: sftp Demo
  5. sftp:
  6.   host: 172.16.3.88 # 服务器ip
  7.   port: 22 # ssh端口
  8.   username: root # 用户名
  9.   password: root # 密码
  10.   # 连接池参数
  11.   pool:
  12.     max-total: 10
  13.     max-idle: 10
  14.     min-idle: 5
复制代码
SftpPoolException.java
  1. package com.vipsoft.sftp.exception;
  2. /**
  3. * sftp连接池异常
  4. */
  5. public class SftpPoolException extends RuntimeException {
  6.     private static final long serialVersionUID = 1L;
  7.     /**
  8.      * Constructs a new runtime exception with {@code null} as its
  9.      * detail message.  The cause is not initialized, and may subsequently be
  10.      * initialized by a call to {@link #initCause}.
  11.      */
  12.     public SftpPoolException() {
  13.     }
  14.     /**
  15.      * Constructs a new runtime exception with the specified detail message.
  16.      * The cause is not initialized, and may subsequently be initialized by a
  17.      * call to {@link #initCause}.
  18.      *
  19.      * @param message the detail message. The detail message is saved for
  20.      *                later retrieval by the {@link #getMessage()} method.
  21.      */
  22.     public SftpPoolException(String message) {
  23.         super(message);
  24.     }
  25.     /**
  26.      * Constructs a new runtime exception with the specified detail message and
  27.      * cause.  <p>Note that the detail message associated with
  28.      * {@code cause} is <i>not</i> automatically incorporated in
  29.      * this runtime exception's detail message.
  30.      *
  31.      * @param message the detail message (which is saved for later retrieval
  32.      *                by the {@link #getMessage()} method).
  33.      * @param cause   the cause (which is saved for later retrieval by the
  34.      *                {@link #getCause()} method).  (A <tt>null</tt> value is
  35.      *                permitted, and indicates that the cause is nonexistent or
  36.      *                unknown.)
  37.      * @since 1.4
  38.      */
  39.     public SftpPoolException(String message, Throwable cause) {
  40.         super(message, cause);
  41.     }
  42.     /**
  43.      * Constructs a new runtime exception with the specified cause and a
  44.      * detail message of <tt>(cause==null ? null : cause.toString())</tt>
  45.      * (which typically contains the class and detail message of
  46.      * <tt>cause</tt>).  This constructor is useful for runtime exceptions
  47.      * that are little more than wrappers for other throwables.
  48.      *
  49.      * @param cause the cause (which is saved for later retrieval by the
  50.      *              {@link #getCause()} method).  (A <tt>null</tt> value is
  51.      *              permitted, and indicates that the cause is nonexistent or
  52.      *              unknown.)
  53.      * @since 1.4
  54.      */
  55.     public SftpPoolException(Throwable cause) {
  56.         super(cause);
  57.     }
  58.     /**
  59.      * Constructs a new runtime exception with the specified detail
  60.      * message, cause, suppression enabled or disabled, and writable
  61.      * stack trace enabled or disabled.
  62.      *
  63.      * @param message            the detail message.
  64.      * @param cause              the cause.  (A {@code null} value is permitted,
  65.      *                           and indicates that the cause is nonexistent or unknown.)
  66.      * @param enableSuppression  whether or not suppression is enabled
  67.      *                           or disabled
  68.      * @param writableStackTrace whether or not the stack trace should
  69.      *                           be writable
  70.      * @since 1.7
  71.      */
  72.     public SftpPoolException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
  73.         super(message, cause, enableSuppression, writableStackTrace);
  74.     }
  75. }
复制代码
config

SftpConfig.java
  1. package com.vipsoft.sftp.config;
  2. import com.vipsoft.sftp.pool.SftpFactory;
  3. import com.vipsoft.sftp.pool.SftpPool;
  4. import com.vipsoft.sftp.utils.SftpUtil;
  5. import org.springframework.boot.context.properties.EnableConfigurationProperties;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. @Configuration
  9. @EnableConfigurationProperties(SftpProperties.class)
  10. public class SftpConfig {
  11.     // 工厂
  12.     @Bean
  13.     public SftpFactory sftpFactory(SftpProperties properties) {
  14.         return new SftpFactory(properties);
  15.     }
  16.     // 连接池
  17.     @Bean
  18.     public SftpPool sftpPool(SftpFactory sftpFactory) {
  19.         return new SftpPool(sftpFactory);
  20.     }
  21.     // 辅助类
  22.     @Bean
  23.     public SftpUtil sftpUtil(SftpPool sftpPool) {
  24.         return new SftpUtil(sftpPool);
  25.     }
  26. }
复制代码
SftpProperties.java
  1. package com.vipsoft.sftp.config;
  2. import com.jcraft.jsch.ChannelSftp;
  3. import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
  4. import org.springframework.boot.context.properties.ConfigurationProperties;
  5. @ConfigurationProperties(prefix = "sftp")
  6. public class SftpProperties {
  7.     private String host;
  8.     private int port = 22;
  9.     private String username = "root";
  10.     private String password = "root";
  11.     private Pool pool = new Pool();
  12.     public String getHost() {
  13.         return host;
  14.     }
  15.     public void setHost(String host) {
  16.         this.host = host;
  17.     }
  18.     public int getPort() {
  19.         return port;
  20.     }
  21.     public void setPort(int port) {
  22.         this.port = port;
  23.     }
  24.     public String getUsername() {
  25.         return username;
  26.     }
  27.     public void setUsername(String username) {
  28.         this.username = username;
  29.     }
  30.     public String getPassword() {
  31.         return password;
  32.     }
  33.     public void setPassword(String password) {
  34.         this.password = password;
  35.     }
  36.     public Pool getPool() {
  37.         return pool;
  38.     }
  39.     public void setPool(Pool pool) {
  40.         this.pool = pool;
  41.     }
  42.     public static class Pool extends GenericObjectPoolConfig<ChannelSftp> {
  43.         private int maxTotal = DEFAULT_MAX_TOTAL;
  44.         private int maxIdle = DEFAULT_MAX_IDLE;
  45.         private int minIdle = DEFAULT_MIN_IDLE;
  46.         public Pool() {
  47.             super();
  48.         }
  49.         @Override
  50.         public int getMaxTotal() {
  51.             return maxTotal;
  52.         }
  53.         @Override
  54.         public void setMaxTotal(int maxTotal) {
  55.             this.maxTotal = maxTotal;
  56.         }
  57.         @Override
  58.         public int getMaxIdle() {
  59.             return maxIdle;
  60.         }
  61.         @Override
  62.         public void setMaxIdle(int maxIdle) {
  63.             this.maxIdle = maxIdle;
  64.         }
  65.         @Override
  66.         public int getMinIdle() {
  67.             return minIdle;
  68.         }
  69.         @Override
  70.         public void setMinIdle(int minIdle) {
  71.             this.minIdle = minIdle;
  72.         }
  73.     }
  74. }
复制代码
Pool

SftpFactory.java
  1. package com.vipsoft.sftp.pool;
  2. import com.jcraft.jsch.ChannelSftp;
  3. import com.jcraft.jsch.JSch;
  4. import com.jcraft.jsch.JSchException;
  5. import com.jcraft.jsch.Session;
  6. import com.vipsoft.sftp.config.SftpProperties;
  7. import com.vipsoft.sftp.exception.SftpPoolException;
  8. import org.apache.commons.pool2.BasePooledObjectFactory;
  9. import org.apache.commons.pool2.PooledObject;
  10. import org.apache.commons.pool2.impl.DefaultPooledObject;
  11. import org.slf4j.Logger;
  12. import org.slf4j.LoggerFactory;
  13. import java.util.Properties;
  14. public class SftpFactory extends BasePooledObjectFactory<ChannelSftp> {
  15.     private  final Logger logger = LoggerFactory.getLogger(this.getClass());
  16.     private SftpProperties properties;
  17.     public SftpProperties getProperties() {
  18.         return properties;
  19.     }
  20.     public void setProperties(SftpProperties properties) {
  21.         this.properties = properties;
  22.     }
  23.     public SftpFactory(SftpProperties properties) {
  24.         this.properties = properties;
  25.     }
  26.     @Override
  27.     public ChannelSftp create() {
  28.         try {
  29.             JSch jsch = new JSch();
  30.             Session sshSession = jsch.getSession(properties.getUsername(), properties.getHost(), properties.getPort());
  31.             sshSession.setPassword(properties.getPassword());
  32.             Properties sshConfig = new Properties();
  33.             sshConfig.put("StrictHostKeyChecking", "no");
  34.             sshSession.setConfig(sshConfig);
  35.             sshSession.connect();
  36.             ChannelSftp channel = (ChannelSftp) sshSession.openChannel("sftp");
  37.             channel.connect();
  38.             return channel;
  39.         } catch (JSchException e) {
  40.             throw new SftpPoolException("连接sfpt失败", e);
  41.         }
  42.     }
  43.     @Override
  44.     public PooledObject<ChannelSftp> wrap(ChannelSftp channelSftp) {
  45.         return new DefaultPooledObject<>(channelSftp);
  46.     }
  47.     // 销毁对象
  48.     @Override
  49.     public void destroyObject(PooledObject<ChannelSftp> p) {
  50.         ChannelSftp channelSftp = p.getObject();
  51.         channelSftp.disconnect();
  52.     }
  53. }
复制代码
SftpPool.java
  1. package com.vipsoft.sftp.pool;
  2. import com.jcraft.jsch.ChannelSftp;
  3. import org.apache.commons.pool2.impl.GenericObjectPool;
  4. public class SftpPool<T> extends GenericObjectPool<ChannelSftp> {
  5.     public SftpPool(SftpFactory factory) {
  6.         super(factory,factory.getProperties().getPool());
  7.     }
  8.     /**
  9.      * 获取一个sftp连接对象
  10.      * @return sftp连接对象
  11.      */
  12.     @Override
  13.     public ChannelSftp borrowObject() throws Exception {
  14.         return super.borrowObject();
  15.     }
  16.     /**
  17.      * 归还一个sftp连接对象
  18.      * @param channelSftp sftp连接对象
  19.      */
  20.     @Override
  21.     public void returnObject(ChannelSftp channelSftp) {
  22.         if (channelSftp!=null) {
  23.             super.returnObject(channelSftp);
  24.         }
  25.     }
  26. }
复制代码
Utils

ByteUtil.java
  1. package com.vipsoft.sftp.utils;
  2. import com.jcraft.jsch.ChannelSftp;
  3. import com.jcraft.jsch.SftpException;
  4. import com.vipsoft.sftp.exception.SftpPoolException;
  5. import com.vipsoft.sftp.pool.SftpPool;
  6. import java.io.InputStream;
  7. public class SftpUtil {
  8.     private SftpPool pool;
  9.     public SftpUtil(SftpPool pool) {
  10.         this.pool = pool;
  11.     }
  12.     /**
  13.      * 下载文件
  14.      *
  15.      * @param dir  远程目录
  16.      * @param name 远程文件名
  17.      * @return 文件字节数组
  18.      */
  19.     public byte[] download(String dir, String name) {
  20.         ChannelSftp sftp = null;
  21.         try {
  22.             sftp = pool.borrowObject();
  23.             sftp.cd(dir);
  24.             InputStream in = sftp.get(name);
  25.             return ByteUtil.inputStreamToByteArray(in);
  26.         } catch (Exception e) {
  27.             throw new SftpPoolException("sftp下载文件出错", e);
  28.         } finally {
  29.             pool.returnObject(sftp);
  30.         }
  31.     }
  32.     /**
  33.      * 上传文件
  34.      *
  35.      * @param dir  远程目录
  36.      * @param name 远程文件名
  37.      * @param in   输入流
  38.      */
  39.     public void upload(String dir, String name, InputStream in) {
  40.         ChannelSftp sftp = null;
  41.         try {
  42.             sftp = pool.borrowObject();
  43.             mkdirs(sftp, dir);
  44.             sftp.cd(dir);
  45.             sftp.put(in, name);
  46.         } catch (Exception e) {
  47.             throw new SftpPoolException("sftp上传文件出错", e);
  48.         } finally {
  49.             pool.returnObject(sftp);
  50.         }
  51.     }
  52.     /**
  53.      * 删除文件
  54.      *
  55.      * @param dir  远程目录
  56.      * @param name 远程文件名
  57.      */
  58.     public void delete(String dir, String name) {
  59.         ChannelSftp sftp = null;
  60.         try {
  61.             sftp = pool.borrowObject();
  62.             sftp.cd(dir);
  63.             sftp.rm(name);
  64.         } catch (Exception e) {
  65.             throw new SftpPoolException("sftp删除文件出错", e);
  66.         } finally {
  67.             pool.returnObject(sftp);
  68.         }
  69.     }
  70.     /**
  71.      * 递归创建多级目录
  72.      *
  73.      * @param dir 多级目录
  74.      */
  75.     private void mkdirs(ChannelSftp sftp, String dir) {
  76.         String[] folders = dir.split("/");
  77.         try {
  78.             sftp.cd("/");
  79.             for (String folder : folders) {
  80.                 if (folder.length() > 0) {
  81.                     try {
  82.                         sftp.cd(folder);
  83.                     } catch (Exception e) {
  84.                         sftp.mkdir(folder);
  85.                         sftp.cd(folder);
  86.                     }
  87.                 }
  88.             }
  89.         } catch (SftpException e) {
  90.             throw new SftpPoolException("sftp创建目录出错", e);
  91.         }
  92.     }
  93. }
复制代码
Test

SftpTest.java
  1. package com.vipsoft.sftp;
  2. import com.vipsoft.sftp.utils.SftpUtil;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. @SpringBootTest
  7. public class SftpTest {
  8.     @Autowired
  9.     private SftpUtil sftpUtil;
  10.     @Test
  11.     void downloadTest() {
  12.         byte[] dockerfiles = sftpUtil.download("/opt/demo/", "Dockerfile");
  13.         System.out.println("FileSize =>" + dockerfiles.length);
  14.     }
  15. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曂沅仴駦

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表