[置顶] apache-common-pool2源码分析
2017-10-31 10:03
435 查看
基础概念
apache-common-pool2是一个对象池管理框架,任何需要对象池这种概念的都可以利用这个框架来实现,例如redis的客户端jedis和dbcp都是基于common-pool2实现的。本文是common-pool2的源码分析,在讲源码前我先阐述一下基本对象池的基本概念吧。现在java创建一个对象的速度已经很快了,但是对于网络连接的创建还是需要很长的时间的,频繁的创建客户端与服务的连接会为系统带来大量的开销。因此,为连接建立缓存机制,就产生了我们现在系统中各种常用的连接池。像各种数据库连接池,服务连接池等。
下面我们就根据common-pool2的源码对对象池的关键环节进行解析。该对象池主要部门就是获取池中对象,将对象归还给池,空闲对象的管理以及对象的销毁这几个环节。
源码解析
0.被池管理的‘对象’下文统一用对象来简称
1.Pool.java 抽象类,
用于使用pool2的实现放池具体实现继承的累初始化池。获取池中对象方法:getResource();将对象归池中:returnResource()。这只是入口,具体的实现后文详细说明。
2.GenericObjectPool ‘对象’的管理者
2.1 borrowObject() ‘对象’获取规则规则详述: while (p == null) { create = false; if (blockWhenExhausted) {//获取新‘对象’阻塞 p = idleObjects.pollFirst();//从空闲队列获取 if (p == null) {//没有可用的空闲队列 p = create();//真实创建,下文再叙 if (p != null) { create = true; } } if (p == null) {//没有从空闲队列获取也没有创建成功(后文会说明why) if (borrowMaxWaitMillis < 0) {//等待时间为-1表示一直等待存在空闲 p = idleObjects.takeFirst(); } else {//加入等待时间,到时间没获取返回null p = idleObjects.pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS); } } if (p == null) { throw new NoSuchElementException( "Timeout waiting for idle object"); } if (!p.allocate()) {//对象相应状态标记修改 p = null; } } else {//不阻塞 p = idleObjects.pollFirst();//获取空闲队列中的对象 if (p == null) { p = create(); if (p != null) { create = true; } } if (p == null) { throw new NoSuchElementException("Pool exhausted"); } if (!p.allocate()) { p = null; } } if (p != null) { try { factory.activateObject(p);//初始化该对象 } catch (Exception e) { try { destroy(p); } catch (Exception e1) { // Ignore - activation failure is more important } p = null; if (create) { NoSuchElementException nsee = new NoSuchElementException( "Unable to activate object"); nsee.initCause(e); throw nsee; } } if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {//getTest***都是配置对象中的值 boolean validate = false; Throwable validationThrowable = null; try { validate = factory.validateObject(p);//检查该对象的有效性,校验规则交由实现方 } catch (Throwable t) { PoolUtils.checkRethrow(t); validationThrowable = t; } if (!validate) { try { destroy(p);//检验后发现不是合法的对象,直接销毁 destroyedByBorrowValidationCount.incrementAndGet(); } catch (Exception e) { // Ignore - validation failure is more important } p = null; if (create) { NoSuchElementException nsee = new NoSuchElementException( "Unable to validate object"); nsee.initCause(validationThrowable); throw nsee; } } } } }
对象真正创建规则
private PooledObject<T> create() throws Exception { int localMaxTotal = getMaxTotal();//获取实现方设置的最大对象存在数量 long newCreateCount = createCount.incrementAndGet(); if (localMaxTotal > -1 && newCreateCount > localMaxTotal || newCreateCount > Integer.MAX_VALUE) {//大于设置的最大数量直接return null createCount.decrementAndGet(); return null; } final PooledObject<T> p; try { p = factory.makeObject();//真正Nnew 对象的地方,交由实现方自己编写 } catch (Exception e) { createCount.decrementAndGet(); throw e; } AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getLogAbandoned()) { p.setLogAbandoned(true); } createdCount.incrementAndGet();//创建对象数量计数 allObjects.put(new IdentityWrapper<T>(p.getObject()), p);//放入对象缓存,以后在取就直接获取,不用再创建 return p; }
2.2对象归还池 returnObject()
public void returnObject(T obj) { PooledObject<T> p = allObjects.get(new IdentityWrapper<T>(obj));//获取对象缓存中的真实的对象 if (p == null) { if (!isAbandonedConfig()) { throw new IllegalStateException( "Returned object not currently part of this pool"); } else { return; // Object was abandoned and removed } } synchronized(p) { final PooledObjectState state = p.getState(); if (state != PooledObjectState.ALLOCATED) {//校验被归还的对象是否再次被归还(没有再次获取前) throw new IllegalStateException( "Object has already been returned to this pool or is invalid"); } else { p.markReturning(); // Keep from being marked abandoned } } long activeTime = p.getActiveTimeMillis();//获取该对象存活时间 if (getTestOnReturn()) {//从配置中获取,由实现方传入,用于回归池校验 if (!factory.validateObject(p)) {//校验失败,直接销毁,不用归还池中 try { destroy(p);//里面代码很简单,就是从allObject和idle中删除,计数相应减一,交由facoty的实现方实现具体的销毁规则 } catch (Exception e) { swallowException(e); } try { ensureIdle(1, false); } catch (Exception e) { swallowException(e); } updateStatsReturn(activeTime); return; } } try { factory.passivateObject(p);//对象 退化处理,就是放入池前,进行相应的处理,例如:释放资源等,交由实现方实现 } catch (Exception e1) { swallowException(e1); try { destroy(p); } catch (Exception e) { swallowException(e); } try { ensureIdle(1, false); } catch (Exception e) { swallowException(e); } updateStatsReturn(activeTime); return; } if (!p.deallocate()) { throw new IllegalStateException( "Object has already been returned to this pool or is invalid"); } int maxIdleSave = getMaxIdle(); if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {//如果空闲对象已经大于配置的数量,直接销毁,不在放入池 try { destroy(p); } catch (Exception e) { swallowException(e); } } else {//放入池中 if (getLifo()) {//根据配置信息,采用FIFO(队列) idleObjects.addFirst(p); } else {//采用FILO(栈) idleObjects.addLast(p); } if (isClosed()) { // Pool closed while object was being added to idle objects. // Make sure the returned object is destroyed rather than left // in the idle object pool (which would effectively be a leak) clear(); } } updateStatsReturn(activeTime);//更新活跃时长 }
2.3 空闲队列定期清理
在实例化该对象时,会调用startEvictor(getTimeBetweenEvictionRunsMillis()/*获取定期检测的时间,由配置传入/)
final void startEvictor(long delay) { synchronized (evictionLock) { if (null != evictor) { EvictionTimer.cancel(evictor);//取消之前的清理任务 evictor = null; evictionIterator = null; } if (delay > 0) {//如果检测时间>0,执行该清理任务 evictor = new Evictor(); EvictionTimer.schedule(evictor, delay, delay); } } } //真正的清理方法 public void evict() throws Exception { assertOpen(); if (idleObjects.size() > 0) {//空闲队列数量>0 PooledObject<T> underTest = null; EvictionPolicy<T> evictionPolicy = getEvictionPolicy();//清理规则 synchronized (evictionLock) { EvictionConfig evictionConfig = new EvictionConfig( getMinEvictableIdleTimeMillis(), getSoftMinEvictableIdleTimeMillis(), getMinIdle()); boolean testWhileIdle = getTestWhileIdle(); for (int i = 0, m = getNumTests()/*每次清理的数量,由配置传入*/; i < m; i++) { //下面就是真正的销毁方法,只是建单的规则校验,并交给factory的实现方真正的销毁的逻辑 if (evictionIterator == null || !evictionIterator.hasNext()) { evictionIterator = new EvictionIterator(idleObjects); } if (!evictionIterator.hasNext()) { // Pool exhausted, nothing to do here return; } try { underTest = evictionIterator.next(); } catch (NoSuchElementException nsee) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce i; i--; evictionIterator = null; continue; } if (!underTest.startEvictionTest()) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce i; i--; continue; } // User provided eviction policy could throw all sorts of // crazy exceptions. Protect against such an exception // killing the eviction thread. boolean evict; try { evict = evictionPolicy.evict(evictionConfig, underTest, idleObjects.size()); } catch (Throwable t) { // Slightly convoluted as SwallowedExceptionListener // uses Exception rather than Throwable PoolUtils.checkRethrow(t); swallowException(new Exception(t)); // Don't evict on error conditions evict = false; } if (evict) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } else { if (testWhileIdle) { boolean active = false; try { factory.activateObject(underTest); active = true; } catch (Exception e) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } if (active) { if (!factory.validateObject(underTest)) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } else { try { factory.passivateObject(underTest); } catch (Exception e) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } } } } if (!underTest.endEvictionTest(idleObjects)) { // TODO - May need to add code here once additional // states are used } } } } } AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getRemoveAbandonedOnMaintenance()) { removeAbandoned(ac); } }
通过上面源码一行行的解读,相信已经把对象池中管理的方式详细讲叙清楚。jedis客户端是基于pool2实现的,阅读jedis的源码 jedis源码分析,相信大家会对连接池具体的实现有很深入和具体的理解。
相关文章推荐
- 【pool】Apache common-pool, common-dbcp源码解读与对象池原理剖析
- cartographer源码分析(10)-common-thread_pool.h
- [转]Apache common-pool, common-dbcp源码解读与对象池原理剖析
- common-pool1.6连接池源码分析流程图
- [置顶] 【Apollo源码分析】系列的第一部分【common】
- 对象池common-pool2源码分析之对象状态
- 对象池common-pool源码分析
- [置顶] 12-总结-【cartographer源码分析】系列的第一部分【common源码分析】
- 对象池common-pool2源码分析
- Apache common-pool, common-dbcp源码解读与对象池原理剖析
- nginx源码分析—内存池结构ngx_pool_t及内存管理
- [置顶] Muduo网络库源码分析之定时器的实现
- apache源码分析入门(一)
- [置顶] 日志审计-apache攻击日志分析
- 6.nginx源码分析之数据结构:ngx_pool_t
- [置顶] Can not perform this action after onSaveInstanceState异常源码分析
- apache-common-pool2(配置参数详解,以及资源回收,从池中获取资源,将资源返还给池 逻辑解析)
- [置顶] 45-总结-【cartographer源码分析】系列的第五部分【kalman_filter】
- 区块链教程btcpool矿池源码分析slparser
- Netty学习之旅------源码分析Netty内存池分配机制初探--PoolArena、PoolChunk、PoolSubpage等数据结构分析