文章首发于公众号「架构师指南」及个人博客 shuyi.tech,欢迎关注访问。
在实际工作中,我们经常会用到各种连接池,例如:连接 FTP 服务器的连接数有限,需要建立一个连接池;连接数据库的连接数有限,需要建立一个连接池。那我们如何去快速实现一个连接池呢?
无论是 FTP 连接池,还是数据库连接池,我们会发现它们都有相同的地方,它们都需要:生命周期管理、连接创建管理等等。如果我们从零开始去实现这些功能,那我们要耗费的时间就很长了!那有没有一个通用的库可以快速实现一个线程池呢?
得益于 Java 完善的生态,前人们针对这种需要开发了一个通用库:Apache Commons Pool(下文简称 ACP)。本质上来说,ACP 库提供的是管理对象池的通用能力,当然也可以用来管理连接池了!
什么是 ACP?ACP 库提供了一整套用于实现对象池化的 API,以及若干种各具特色的对象池实现。目前最常用的版本是 2.0 版本,相对于 1.x 版本而言,并不是简单升级。2.0 版本是对象池实现的完全重写,显著的提升了性能和可伸缩性,并且包含可靠的实例跟踪和池监控。
Apache Commons Pool 的官网地址为:Pool – Overview,想翻找相关文档资料,到这里去是最权威、最全面的。
如何使用 ACP?要使用 ACP 实现一个线程池,首先需要先引入 ACP 的依赖包,这里以 Maven 为例。
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.0</version> </dependency>要使用 ACP 实现一个对象池,大致可以分为三个步骤:
创建对象工厂:告诉 ACP 如何创建你要的对象。
创建对象池:告诉 ACP 你想创建一个怎样的对象池。
使用对象池:ACP 告诉你如何使用你的对象。
创建对象工厂 文章首发于公众号「架构师指南」及个人博客 shuyi.tech,欢迎关注访问。对象工厂告诉 ACP,它应该如何去创建、激活、钝化、销毁你的对象。创建对象工厂非常简单,只需要实现 ACP 的 PooledObjectFactory 接口即可。PooledObjectFactory 接口的定义如下:
public interface PooledObjectFactory<T> { PooledObject<T> makeObject() throws Exception; void destroyObject(PooledObject<T> p) throws Exception; boolean validateObject(PooledObject<T> p); void activateObject(PooledObject<T> p) throws Exception; void passivateObject(PooledObject<T> p) throws Exception; }但更多情况下,我们会继承 BasePooledObjectFactory 类来实现对象工厂。因为 BasePooledObjectFactory 类是 PooledObjectFactory 的基础实现类,使用它可以帮我们省了很多麻烦。通过继承这个抽象类,我们只需要实现两个方法:create() 和 wrap() 方法。
// 告诉 ACP 如何创建对象 public abstract T create() throws Exception; // 定义你要返回的对象 public abstract PooledObject<T> wrap(T obj);create() 方法定义你的对象初始化过程,最后将初始化完成的对象返回。例如你想定义一个 SFTP 的连接,那么你首先需要定义一个 JSch 对象,之后设置账号密码,之后连接服务器,最后返回一个 ChannelSftp 对象。
public ChannelSftp create() { // SFTP 连接的创建过程 }wrap() 方法定义你要返回的对象,对于一个 SFTP 的连接池来说,其实就是一个 ChannelSftp 对象。一般情况下可以使用类 DefaultPooledObject 替代,参考实现如下:
@Override public PooledObject<Foo> wrap(Foo foo) { return new DefaultPooledObject<Foo>(foo); } 创建对象池创建好对象工厂之后,ACP 已经知道你需要的对象如何创建了。那么接下来,你需要根据你的实际需要,去创建一个对象池。在 ACP 中,我们通过 GenericObjectPool 以及 GenericObjectPoolConfig 来创建一个对象池。
// 声明一个对象池 private GenericObjectPool<ChannelSftp> sftpConnectPool; // 设置连接池配置 GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); poolConfig.setEvictionPolicyClassName("tech.shuyi.javacodechip.acp.SftpEvictionPolicy"); poolConfig.setBlockWhenExhausted(true); poolConfig.setJmxEnabled(false); poolConfig.setMaxWaitMillis(1000 * 10); poolConfig.setTimeBetweenEvictionRunsMillis(60 * 1000); poolConfig.setMinEvictableIdleTimeMillis(20 * 1000); poolConfig.setTestWhileIdle(true); poolConfig.setTestOnReturn(true); poolConfig.setTestOnBorrow(true); poolConfig.setMaxTotal(3); // 设置抛弃策略 AbandonedConfig abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnMaintenance(true); abandonedConfig.setRemoveAbandonedOnBorrow(true); this.sftpConnectPool = new GenericObjectPool<>(sftpConnectFactory, poolConfig, abandonedConfig);