⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7289344148195835961 「徐二七」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

1 一些废话

作为一名Java开发人员,池化技术或多或少在业务代码中使用。常见的包括线程池、连接池等。也是因为Java语言超级丰富的基建,基本上这些池化能力都有着相对成熟的“工具”。

比如,需要使用线程池的时候常常会选择Spring提供的 ThreadPoolTaskExecutor , 工具内部替我们维护了线程的生命周期与任务的状态变化。

线程池的运转流程图

2 正文开始

在笔者的业务场景里,java服务需要通过命令行启动一个特殊进程,并在进程使用完后将其销毁。而业务对启动这个进程的整体耗时较为敏感,打算利用池化技术,将进程池化复用,去除启动进程的消耗,达到优化性能的目标。

认识 GenericObjectPool

池化技术的概念大家可能都比较熟悉了,但真正要从零开始实现池化能力,就会感觉困难很多。好在Java丰富的基建在提供ThreadPoolTaskExecutor的同时,也提供了GenericObjectPool这个辅助我们实现自定义对象池化的工具。顺带提一句:JedisPool就是使用这个工具实现的。

GenericObjectPool构造方法一共就3个参数,只有PooledObjectFactory必传;

/**
* Creates a new {@code GenericObjectPool} that tracks and destroys
* objects that are checked out, but never returned to the pool.
*
* @param factory The object factory to be used to create object instances
* used by this pool
* @param config The base pool configuration to use for this pool instance.
* The configuration is used by value. Subsequent changes to
* the configuration object will not be reflected in the
* pool.
* @param abandonedConfig Configuration for abandoned object identification
* and removal. The configuration is used by value.
*/
public GenericObjectPool(final PooledObjectFactory<T> factory,
final GenericObjectPoolConfig<T> config, final AbandonedConfig abandonedConfig) {
}

PooledObjectFactory 按照方法注释的描述,它是专门负责给池子创建对象实例的。当然除了创建对象(makeObject), 还包括了检验、激活、销毁对象。基本涵盖了对象生命周期中的各个阶段。

void activateObject(PooledObject<T> p) throws Exception;

void destroyObject(PooledObject<T> p) throws Exception;

PooledObject<T> makeObject() throws Exception;

void passivateObject(PooledObject<T> p) throws Exception;

boolean validateObject(PooledObject<T> p);

更加详细的说明可以浏览 GenericObjectPool’s apidocs[1]。源码的注释也很详细值得一看。

使用 GenericObjectPool

先引入依赖

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${version}</version>
</dependency>

根据自身业务实现PooledObjectFactory接口;作者的业务场景是进程池化,那么对应的创建对象、销毁对象的方法就是创建进程和销毁进程的代码。

public class MyProcessFactory implements PooledObjectFactory<MyProcess> {
@Override
public void destroyObject(PooledObject<MyProcess> p) throws Exception {
final MyProcess process = p.getObject();
if (null != process) {
// 销毁进程
process.stop();
}
}

@Override
public PooledObject<MyProcess> makeObject() throws Exception {
// 这里就是去创建一个进程
MyProcess process = new MyProcess();
process.start();
return new DefaultPooledObject<>(process);
}

// 剩下几个方法也可以按需实现
}

下一步就是构建 GenericObjectPool 实例

PooledObjectFactory<MyProcess> factory = new MyProcessFactory();
GenericObjectPool<MyProcess> pool = new GenericObjectPool(factory);

使用GenericObjectPool

// 获取进程实例
MyProcess process = pool.borrowObject();

// 归还实例
pool.returnObject(process);

进阶使用 GenericObjectPoolConfig

顾名思义,GenericObjectPoolConfig是池化工具的配置类;它包含了池的最大容量、池的最大空闲数、最小空闲数等核心参数。除此之外在它的父类 BaseObjectPoolConfig 中,空闲对象检测规则,对象存放队列进出规则(LIFO)等更加细节的配置。

/**
* The default value for the {@code maxTotal} configuration attribute.
* @see GenericObjectPool#getMaxTotal()
*/
public static final int DEFAULT_MAX_TOTAL = 8;

/**
* The default value for the {@code maxIdle} configuration attribute.
* @see GenericObjectPool#getMaxIdle()
*/
public static final int DEFAULT_MAX_IDLE = 8;

/**
* The default value for the {@code minIdle} configuration attribute.
* @see GenericObjectPool#getMinIdle()
*/
public static final int DEFAULT_MIN_IDLE = 0;

通过调整这些参数值,就能创建符合业务要求的池子。下面就是能常驻4个进程的一套配置参数。

private GenericObjectPoolConfig<MyProcess> genericObjectPoolConfig() {
final GenericObjectPoolConfig<MyProcess> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(20); // 池的最大容量
config.setMaxIdle(4); // 最大空闲连接数
config.setMinIdle(0); // 最小空闲连接数
config.setMaxWait(Duration.ofSeconds(5)); // 获取对象时最大等待时间
config.setTimeBetweenEvictionRuns(Duration.ofMinutes(1)); // 空闲对象检查间隔
config.setMinEvictableIdleTime(Duration.ofMinutes(10)); // 空闲对象被移除的最小空闲时间
config.setTestOnBorrow(true);
config.setLifo(false);
return config;
}

3 后续

当然真实的业务中还会有很多不相关的逻辑夹杂其中,上文基本涵盖了池化对象搭建与配置的实现方法。最终也实现了性能优化的目标。希望此文能为大家在池化运用多些帮助。

文章目录
  1. 1. 1 一些废话
    1. 1.1. 线程池的运转流程图
  2. 2. 2 正文开始
    1. 2.1. 认识 GenericObjectPool
    2. 2.2. 使用 GenericObjectPool
    3. 2.3. 进阶使用 GenericObjectPoolConfig
  3. 3. 3 后续