基于Apache组件,分析对象池原理

2023-01-23 0 304

水池里养:Object;

一、结构设计与基本原理

1、此基础事例

具体来说看两个如前所述common-pool2第一类池模块的应用领域事例,主要就有厂房类、第一类池、第一类四个核心理念配角,和韦格尔第一类的采用业务流程:

import

org.apache.commons.pool2.BasePooledObjectFactory;

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;

import

org.slf4j.Logger;

import

org.slf4j.LoggerFactory;

public class ObjPool

{

public static void main(String[] args) throws Exception

{

// 声明第一类池DevObjPool devObjPool =new

DevObjPool() ;

// 池中借用第一类

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 new

DevObj() ;

}

@Overridepublic PooledObject<DevObj> wrap(DevObj devObj)

{

// 韦格尔第一类return new

DefaultPooledObject<>(devObj);

}

}

/**

* 第一类池

*/
class DevObjPool extends GenericObjectPool<DevObj>

{

public DevObjPool()

{

super(new DevObjFactory(), new

GenericObjectPoolConfig<>());

}

}

事例中第一类是完全自定义的;第一类厂房中则重写两个核心理念方法:创建和包装,以此创建韦格尔第一类;第一类池的构建依赖定义的第一类厂房,配置采用模块提供的常规配置类;可以通过调整第一类实例化的时间和创建第一类的个数,初步理解第一类池的基本原理。

2、接口结构设计

1.1 PooledObjectFactory 接口

厂房类,负责第一类实例化,创建、验证、销毁、状态管理等;事例中BasePooledObjectFactory类则是该接口的此基础实现;

1.2 ObjectPool 接口

第一类池,并且继承Closeab

1.3 PooledObject 接口

韦格尔第一类,如前所述包装类被维护在第一类池中,并且维护一些附加信息用来跟踪,例如时间、状态;事例中采用DefaultPooledObject包装类,实现该接口并且线程安全,注意厂房类中的重写;

3、运行基本原理

基于Apache组件,分析对象池原理

二、构造预测

1、第一类池

public GenericObjectPool(final PooledObjectFactory<T> factory,final GenericObjectPoolConfig<T> config)

;

在完整的构造方法中,涉及到四个核心理念第一类:厂房第一类、配置第一类、双端阻塞队列;通过这几个第一类创建两个新的第一类池;在config中提供了一些简单的默认配置:例如maxTotal、maxIdle、minIdle等,也可以扩展自定义配置;

2、双端队列

private final

LinkedBlockingDeque> idleObjects;

public GenericObjectPool(finalPooledObjectFactory<T> factory,final GenericObjectPoolConfig<T> config)

{

idleObjects = new

LinkedBlockingDeque<>(config.getFairness());

}

LinkedBlockingDeque支持在队列的首尾操作元素,例如添加和移除等;操作需要通过主锁进行加锁,并且如前所述两个状态锁进行协作;

// 队首节点private transient

LinkedBlockingDeque.Node first;

// 队尾节点private transient

LinkedBlockingDeque.Node last;

// 主锁private final

InterruptibleReentrantLock lock;

// 非空状态锁private final

Condition notEmpty;

// 未满状态锁private final

Condition notFull;

关于链表和队列的特点,在之前的文章中有单独预测过,此处的源码在JDK的容器中也很常见,这里不在赘述,第一类池的整个构造有大致轮廓之后,下面再来细看第一类的管理逻辑。

三、第一类管理

1、添加第一类

创建两个新第一类并且放入池中,通常应用领域在需要预加载的场景中;涉及到两个核心理念操作:厂房创建第一类,第一类韦格尔管理;

public voidGenericObjectPool.addObject()throws

Exception ;

2、借用第一类

public T GenericObjectPool.borrowObject(final long borrowMaxWaitMillis) throws

Exception ;

基于Apache组件,分析对象池原理

3、归还第一类

public void GenericObjectPool.returnObject(final

T obj) ;

基于Apache组件,分析对象池原理

归还第一类的时候,具体来说转换为韦格尔第一类和标记RETURNING状态;经过多次校验判断,如果失败则销毁该第一类,并重新维护第一类池中可用的空闲第一类;最终第一类被标记为空闲状态,如果不超出最大空闲数,则第一类被放到队列的某一端;

4、第一类状态

关于韦格尔第一类的状态在PooledObjectState类中有枚举和描述,在图中只是对部分几个状态流转做示意,更多细节可以参考状态类;

基于Apache组件,分析对象池原理

可以参考在上述事例中采用到的DefaultPooledObject默认韦格尔第一类类中相关方法,结合状态枚举,可以理解不同状态之间的校验和转换。

四、Redis应用领域

Lettuce作为Redis高级的客户端模块,通信层采用Netty模块,并且是线程安全,支持同步和异步模式,支持集群和哨兵模式;作为当下项目中常用的配置,其底层第一类池如前所述common-pool2模块。

1、配置管理

如前所述如下配置即表示采用Lettuce模块,其中涉及到池的几个参数配置:最小空闲、最大活跃、最大空闲;这里可以对比GenericObjectPoolConfig中的配置:

spring:redis:host: ${REDIS_HOST:127.0.0.1}lettuce:pool:min-idle: 10max-active: 100max-idle: 100

2、源码预测

围绕第一类池的特点,自然去追寻源码中关于:配置、厂房、第一类几个核心理念的配角类;从上述配置参数切入,可以很容易发现如下几个类:

基于Apache组件,分析对象池原理

2.1 配置转换

// 连接配置class LettuceConnectionConfiguration extends RedisConnectionConfiguration

{

private static class PoolBuilderFactory

{

// 构建第一类池配置private

GenericObjectPoolConfig getPoolConfig(RedisProperties.Pool properties) {

GenericObjectPoolConfig<?> config = new

GenericObjectPoolConfig<>();

config.setMaxTotal(properties.getMaxActive());

config.setMaxIdle(properties.getMaxIdle());

config.setMinIdle(properties.getMinIdle());

return

config;

}

}

}

这里将配置文件中Redis的相关参数,构建到GenericObjectPoolConfig类中,即配置加载过程;

2.2 第一类池构造

class LettucePoolingConnectionProvider implements LettuceConnectionProvider

{

// 第一类池核心理念配角private final

GenericObjectPoolConfig poolConfig;

private final

BoundedPoolConfig 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 第一类管理

class LettucePoolingConnectionProvider implements LettuceConnectionProvider

{

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

基于Apache组件,分析对象池原理
举报/反馈

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务