水池里养:Object;
一、结构设计与基本原理
1、此基础事例
具体来说看两个如前所述common-pool2第一类池模块的应用领域事例,主要就有厂房类、第一类池、第一类四个核心理念配角,和韦格尔第一类的采用业务流程:
org.apache.commons.pool2.BasePooledObjectFactory;
importorg.apache.commons.pool2.PooledObject;
importorg.apache.commons.pool2.impl.DefaultPooledObject;
importorg.apache.commons.pool2.impl.GenericObjectPool;
importorg.apache.commons.pool2.impl.GenericObjectPoolConfig;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
public class ObjPool{
public static void main(String[] args) throws Exception{
// 声明第一类池DevObjPool devObjPool =newDevObjPool() ;
// 池中借用第一类DevObj devObj = devObjPool.borrowObject();
System.out.println(“Idle=”+devObjPool.getNumIdle()+“;Active=”+devObjPool.getNumActive());
// 采用第一类devObj.devObjInfo();
// 归还给第一类池devObjPool.returnObject(devObj);
System.out.println(“Idle=”+devObjPool.getNumIdle()+“;Active=”+devObjPool.getNumActive());
// 查看第一类池System.out.println(devObjPool.listAllObjects());
}
}
/**
* 第一类定义
*/ class DevObj{
private static final Logger logger = LoggerFactory.getLogger(DevObj.class);
public DevObj (){
logger.info(“build…dev…obj”);
}
public void devObjInfo (){
logger.info(“dev…obj…info”);
}
}
/**
* 第一类厂房
*/ class DevObjFactory extends BasePooledObjectFactory<DevObj>{
@Overridepublic DevObj create() throws Exception{
// 创建第一类return newDevObj() ;
}
@Overridepublic PooledObject<DevObj> wrap(DevObj devObj){
// 韦格尔第一类return newDefaultPooledObject<>(devObj);
}
}
/**
* 第一类池
*/ class DevObjPool extends GenericObjectPool<DevObj>{
public DevObjPool(){
super(new DevObjFactory(), newGenericObjectPoolConfig<>());
}
}
事例中第一类是完全自定义的;第一类厂房中则重写两个核心理念方法:创建和包装,以此创建韦格尔第一类;第一类池的构建依赖定义的第一类厂房,配置采用模块提供的常规配置类;可以通过调整第一类实例化的时间和创建第一类的个数,初步理解第一类池的基本原理。
2、接口结构设计
1.1 PooledObjectFactory 接口
1.2 ObjectPool 接口
1.3 PooledObject 接口
3、运行基本原理

二、构造预测
1、第一类池
;
在完整的构造方法中,涉及到四个核心理念第一类:厂房第一类、配置第一类、双端阻塞队列;通过这几个第一类创建两个新的第一类池;在config中提供了一些简单的默认配置:例如maxTotal、maxIdle、minIdle等,也可以扩展自定义配置;
2、双端队列
LinkedBlockingDeque
{
idleObjects = newLinkedBlockingDeque<>(config.getFairness());
}
LinkedBlockingDeque支持在队列的首尾操作元素,例如添加和移除等;操作需要通过主锁进行加锁,并且如前所述两个状态锁进行协作;
LinkedBlockingDeque.Node
LinkedBlockingDeque.Node
InterruptibleReentrantLock lock;
// 非空状态锁private finalCondition notEmpty;
// 未满状态锁private finalCondition notFull;
关于链表和队列的特点,在之前的文章中有单独预测过,此处的源码在JDK的容器中也很常见,这里不在赘述,第一类池的整个构造有大致轮廓之后,下面再来细看第一类的管理逻辑。
三、第一类管理
1、添加第一类
创建两个新第一类并且放入池中,通常应用领域在需要预加载的场景中;涉及到两个核心理念操作:厂房创建第一类,第一类韦格尔管理;
Exception ;
2、借用第一类
Exception ;

3、归还第一类
T obj) ;

归还第一类的时候,具体来说转换为韦格尔第一类和标记RETURNING状态;经过多次校验判断,如果失败则销毁该第一类,并重新维护第一类池中可用的空闲第一类;最终第一类被标记为空闲状态,如果不超出最大空闲数,则第一类被放到队列的某一端;
4、第一类状态
关于韦格尔第一类的状态在PooledObjectState类中有枚举和描述,在图中只是对部分几个状态流转做示意,更多细节可以参考状态类;

可以参考在上述事例中采用到的DefaultPooledObject默认韦格尔第一类类中相关方法,结合状态枚举,可以理解不同状态之间的校验和转换。
四、Redis应用领域
Lettuce作为Redis高级的客户端模块,通信层采用Netty模块,并且是线程安全,支持同步和异步模式,支持集群和哨兵模式;作为当下项目中常用的配置,其底层第一类池如前所述common-pool2模块。
1、配置管理
如前所述如下配置即表示采用Lettuce模块,其中涉及到池的几个参数配置:最小空闲、最大活跃、最大空闲;这里可以对比GenericObjectPoolConfig中的配置:
2、源码预测
围绕第一类池的特点,自然去追寻源码中关于:配置、厂房、第一类几个核心理念的配角类;从上述配置参数切入,可以很容易发现如下几个类:

2.1 配置转换
{
private static class PoolBuilderFactory{
// 构建第一类池配置privateGenericObjectPoolConfig getPoolConfig(RedisProperties.Pool properties) {
GenericObjectPoolConfig<?> config = newGenericObjectPoolConfig<>();
config.setMaxTotal(properties.getMaxActive());
config.setMaxIdle(properties.getMaxIdle());
config.setMinIdle(properties.getMinIdle());
returnconfig;
}
}
}
这里将配置文件中Redis的相关参数,构建到GenericObjectPoolConfig类中,即配置加载过程;
2.2 第一类池构造
{
// 第一类池核心理念配角private finalGenericObjectPoolConfig poolConfig;
private finalBoundedPoolConfig asyncPoolConfig;
private final Map<Class<?>, GenericObjectPool> pools = new ConcurrentHashMap(32);
LettucePoolingConnectionProvider(LettuceConnectionProvider provider, LettucePoolingClientConfiguration config) {
this.poolConfig = clientConfiguration.getPoolConfig();
this.asyncPoolConfig = CommonsPool2ConfigConverter.bounded(this.config);
}
}
2.3 第一类管理
{
public<T extends StatefulConnection<?, ?>>T getConnection(Class<T> connectionType){
GenericObjectPool pool = (GenericObjectPool)this.pools.computeIfAbsent();
StatefulConnection connection = (StatefulConnection)pool.borrowObject();
}
// 释放Redis连接public void release(StatefulConnection<?, ?> connection){
GenericObjectPool<StatefulConnection<?, ?>> pool = (GenericObjectPool)this.poolRef.remove(connection);
}
}
据相关配置创建池第一类,并维护到Map容器中,然后从池中借用Redis连接第一类;释放第一类时具体来说判断第一类所属的池,将第一类归还到相应的池中。
最后总结,本文从第一类池的两个简单事例切入,主要就预测common-pool2模块关于:池、厂房、配置、第一类管理几个配角的源码逻辑,并且参考其在Redis中的实践,只是冰山一角,像这种通用型并且应用领域范围广的模块,很值得时常去读一读源码,真的令人惊叹其鬼斧天工的结构设计。
五、参考源码
应用领域仓库:
https://gitee.com/cicadasmile/butte-flyer-parent
模块封装:
https://gitee.com/cicadasmile/butte-frame-parent
