基于commons-pool2源码了解连接池实现原理

1.背景

       最近查看了下Apache commons-pool2的源代码commons-pool2-2.4.2,代码不多,大概50个java类左右,阅读源代码的初衷是为了通过不断的学习和总结,升华自己的技术能力,写此博客是为了给自己留下一点笔记,日后能够温故知新。

       Apache commons-pool2类库是对象池技术的一种具体实现,它的出现是为了解决频繁的创建和销毁对象带来的性能损耗问题,原理就是建立一个对象池,池中预先生成了一些对象,需要对象的时候进行租借,用完后进行归还,对象不够时灵活的自动创建,对象池满后提供参数控制是否阻塞还是非阻塞响应租借.

       熟悉了Apache commons-pool2对于了解数据库连接池DBCP和了解redis客户端jedis的连接池都有很大帮助,因为jedis的连接池就是基于Apache commons-pool2实现,而DBCP是基于Jakarta commons-pool实现。

 

2.Apache commons-pool2中3类重要接口

  •  PooledObjectFactory/KeyedPooledObjectFactory:是两个接口,作用都是产生PooledObject的工厂,定义了如何makeObject创建、destroyObject销毁、validateObject校验、activateObject激活PooledObject对象,使用Apache commons-pool2的使用者需要自己实现这个接口
  • PooledObject:是一个接口,定义了getCreateTime获取PooledObject创建时间,getActiveTimeMillis获取PooledObject处于激活状态的时间,getIdleTimeMillis获取PooledObject空闲时间,getLastBorrowTime获取PooledObject最近借出时间,getLastReturnTime获取PooledObject最近归还时间,getLastUsedTime获取PooledObject最近使用时间。目前Apache commons-pool2提供了2个默认实现DefaultPooledObject和PooledSoftReference,一般使用DefaultPooledObject即可
  • ObjectPool/KeyedObjectPool:是两个接口,作用都是管理池里面的PooledObject,borrowObject借出PooledObject,returnObject归还PooledObject,invalidateObject调用PooledObjectFactory销毁PooledObject,addObject调用PooledObjectFactory创建PooledObject,getNumIdle给出PooledObject空闲个数,getNumActive给出PooledObject激活的个数,使用Apache commons-pool2的使用者可以使用默认的5个实现(SoftReferenceObjectPool GenericObjectPool ProxiedObjectPool GenericKeyedObjectPool ProxiedKeyedObjectPool),也可以自己实现

       其中PooledObjectFactory PooledObject ObjectPool关系和作用如下图: 
       其中KeyedPooledObjectFactory PooledObject KeyedObjectPool关系和作用如下图: 

 

3.PooledObject接口类继承关系OOM图

       PooledObject:是一个接口,定义了getCreateTime获取PooledObject创建时间,getActiveTimeMillis获取PooledObject处于激活状态的时间,getIdleTimeMillis获取PooledObject空闲时间,getLastBorrowTime获取PooledObject最近借出时间,getLastReturnTime获取PooledObject最近归还时间,getLastUsedTime获取PooledObject最近使用时间。目前Apache commons-pool2提供了2个默认实现DefaultPooledObject和PooledSoftReference,一般使用DefaultPooledObject即可 


  

4.对象池接口ObjectPool和KeyedObjectPool类关系

       Apache commons-pool2里有针对对象池的5个具体实现类SoftReferenceObjectPool GenericObjectPool ProxiedObjectPool GenericKeyedObjectPool ProxiedKeyedObjectPool ,这5个对象池按照实现的接口不同分为2类,一个是实现了接口ObjectPool,另一个是实现了接口KeyedObjectPool

    4.1 PowerDesigner反向工程ObjectPool的OOM关系图

  • GenericObjectPool:可配置LIFO/FIFO行为的ObjectPool的实现。默认采用LIFO队列方式。这意味着当有闲置的可用对象在对象池中时,borrowObject方法会返回最近的实例。如果配置文件中的LIFO配置项的值为false,则将返回相反排序的实例,也就是会返回最先进入对象池的对象的实例;
  • SoftReferenceObjectPool:使用LIFO行为实现的ObjectPool。此外,在这个对象池实现中,每个对象都会被包装到一个SoftReference中。SoftReference允许垃圾回收机制在需要释放内存时回收对象池中的对象;
  • ProxiedObjectPool:使用CGLIB或者JDK自带动态代理技术,代理由GenericObjectPool或者SoftReferenceObjectPool产生的ObjectPool对象

  

    4.2 PowerDesigner反向工程KeyedObjectPool的OOM关系图

  • GenericKeyedObjectPool:可配置LIFO/FIFO行为的ObjectPool的实现。默认采用LIFO队列方式。这意味着当有闲置的可用对象在对象池中时,borrowObject方法会返回最近的实例。如果配置文件中的LIFO配置项的值为false,则将返回相反排序的实例,也就是会返回最先进入对象池的对象的实例;
  • ProxiedKeyedObjectPool:使用CGLIB或者JDK自带动态代理技术,代理由GenericKeyedObjectPool产生的ObjectPool对象

5. PooledObjectState类枚举的PooledObject状态及其转换关系

状态描述
IDLE位于队列中,未使用
ALLOCATED在使用
EVICTION位于队列中,当前正在测试,可能会被回收
EVICTION_RETURN_TO_HEAD

不在队列中,当前正在测试,可能会被回收。

如果客户端试图借出该正在被测试的对象,

需等到测试完毕后才能借出并且从队列中移除;

待测试完毕后,如果没被回收该对象会放置在队列的开头位置。

VALIDATION位于队列中,当前正在验证
VALIDATION_PREALLOCATED

不在队列中,当前正在验证。当对象从池中被借出,

在配置了testOnBorrow的情况下,对像从队列移除和进行预分配的时候会进行验证

VALIDATION_RETURN_TO_HEAD

不在队列中,正在进行验证。从池中借出对象时,

从队列移除对象时会先进行测试。返回到队列头部的时候应该做一次完整的验证

INVALID回收或验证失败,将销毁
ABANDONED即将无效
RETURNING返还到池中



 

6.GenericObjectPool的BorrowObject和ReturnObject流程分析

       这里先来一张GenericObjectPool的组图,如下: 

 

    6.1 GenericObjectPool,BorrowObject租借池化对象     6.2 GenericObjectPool.ReturnObject归还池化对象  

7.GenericObjectPoolConfig配置属性介绍

       GenericObjectPoolConfig是一个配置类,提供了很多参数来控制对象池的相关属性,如果你使用过jedis,对于其JedisPoolConfig可能不陌生

[xml] view plain copy

 

  1. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">  
  2.     <property name="maxTotal" value="2048" />  
  3.     <property name="maxIdle" value="200" />  
  4.     <property name="numTestsPerEvictionRun" value="1024" />  
  5.     <property name="timeBetweenEvictionRunsMillis" value="30000" />  
  6.     <property name="minEvictableIdleTimeMillis" value="-1" />  
  7.     <property name="softMinEvictableIdleTimeMillis" value="10000" />  
  8.     <property name="maxWaitMillis" value="1500" />  
  9.     <property name="testOnBorrow" value="true" />  
  10.     <property name="testWhileIdle" value="true" />  
  11.     <property name="testOnReturn" value="false" />  
  12.     <property name="jmxEnabled" value="true" />  
  13.     <property name="blockWhenExhausted" value="false" />  
  14. </bean>  

       而JedisPoolConfig其实就是继承自GenericObjectPoolConfig,GenericObjectPoolConfig的相关配置属性列表如下: 

属性类型默认值作用
maxTotalint8池中最多可用的实例个数
maxIdleint8池中最多可容纳的实例(instances)个数
minIdleint0池中最少需要容纳的实例(instances)个数
lifobooleanTRUE池中实例的操作是否按照LIFO(后进先出)的原则
fairnessbooleanFALSE租借池化对象的客户端按照FIFO进行
maxWaitMillislong-1调用borrowObject方法时,需要等待的最长时间
minEvictableIdleTimeMillislong1800000池中对象处于空闲状态开始到被回收的最短时间
softMinEvictableIdleTimeMillislong3池中对象处于空闲状态开始到被回收的最短时间
numTestsPerEvictionRunint3池中处于空闲状态的对象每次被检测是否回收时
最多只检测3个处于空闲状态的对象,比如该值设置为3,此时池中有5个闲置对象,那么每次只会检查前三个闲置对象
evictionPolicyClassNameStringorg.apache.commons.pool2.
impl.DefaultEvictionPolicy
回收策略
testOnCreatebooleanFALSE调用borrowObject方法时,依据此标识判断是否
需要对返回的结果进行校验,如果校验失败会删
除当前实例,并尝试再次获取
testOnBorrowbooleanFALSE调用borrowObject方法时,依据此标识判断是否
需要对返回的结果进行校验,如果校验失败会
删除当前实例,并尝试再次获取
testOnReturnbooleanFALSE调用returnObject方法时,依据此标识判断是否
需要对返回的结果进行校验
testWhileIdlebooleanFALSE闲置实例校验标识,如果校验失败会删除当前实例
timeBetweenEvictionRunsMillislong-1闲置实例校验器启动的时间间隔,单位是毫秒
blockWhenExhaustedbooleanTRUE当池中对象都被借出后,客户端来租借对象,
此时是否进行阻塞还是非阻塞,默认阻塞
jmxEnabledbooleanTRUE开启JMX开关
jmxNamePrefixStringpoolJMX前缀
jmxNameBaseStringnullJMX根名字
  • 网友遇到的问题1:mysql服务端设置了连接8小时失效,但是commons-pool2对应的对象池中没有配置上timeBetweenEvictionRunsMillis minEvictableIdleTimeMillis numTestsPerEvictionRun,导致没有对池化的mysql客户端进行检测,所以经验是服务器端如果设置了idel>0的空闲时间, 那么客户端最好设置上对应的心跳频率即多久心跳一次;
  • 网友遇到的问题2:redis的服务端设置了timeout=0,由于网络原因,commons-pool2已经将池中redis客户端销毁,但是服务端redis因为配置了timeout=0禁用了关闭限制的redis客户端功能,导致服务端大量僵尸进程存在,所以经验是配置redis服务端的timeout为一个大于0的值,意思是客户端如果空闲了且空闲时间大于该值,服务端就会关闭该连接

 

文章转载自:https://blog.csdn.net/zilong_zilong/article/details/78556281

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值