`
QING____
  • 浏览: 2250780 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Apache commons-pool对象池原理分析

    博客分类:
  • JAVA
 
阅读更多

    Apache commons-pool本质上是"对象池",即通过一定的规则来维护对象集合的容器;commos-pool在很多场景中,用来实现"连接池"/"任务worker池"等,大家常用的dbcp数据库连接池,也是基于commons-pool实现.

    commons-pool实现思想非常简单,它主要的作用就是将"对象集合"池化,任何通过pool进行对象存取的操作,都会严格按照"pool配置"(比如池的大小)实时的创建对象/阻塞控制/销毁对象等.它在一定程度上,实现了对象集合的管理以及对象的分发.

    1) 将创建对象的方式,使用工厂模式;

    2) 通过"pool配置"来约束对象存取的时机

    3) 将对象列表保存在队列中(LinkedList)

 

    首选需要声明,不同的"对象池"(或者连接池)在设计上可能存在很大的区别,但是在思想上大同小异,本文主要讲解commons-pool,它和其他"连接池"的区别在此不多讨论.

 

一.对象生命周期



 

 

二.Config详解:

  1. maxActive: 链接池中最大连接数,默认为8.
  2. maxIdle: 链接池中最大空闲的连接数,默认为8.
  3. minIdle: 连接池中最少空闲的连接数,默认为0.
  4. maxWait: 当连接池资源耗尽时,调用者最大阻塞的时间,超时将跑出异常。单位,毫秒数;默认为-1.表示永不超时.
  5. minEvictableIdleTimeMillis: 连接空闲的最小时间,达到此值后空闲连接将可能会被移除。负值(-1)表示不移除。
  6. softMinEvictableIdleTimeMillis: 连接空闲的最小时间,达到此值后空闲链接将会被移除,且保留“minIdle”个空闲连接数。默认为-1.
  7. numTestsPerEvictionRun: 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3.
  8. testOnBorrow: 向调用者输出“链接”资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。默认为false。建议保持默认值.
  9. testOnReturn:  向连接池“归还”链接时,是否检测“链接”对象的有效性。默认为false。建议保持默认值.
  10. testWhileIdle:  向调用者输出“链接”对象时,是否检测它的空闲超时;默认为false。如果“链接”空闲超时,将会被移除。建议保持默认值.
  11. timeBetweenEvictionRunsMillis:  “空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1.
  12.  whenExhaustedAction: 当“连接池”中active数量达到阀值时,即“链接”资源耗尽时,连接池需要采取的手段, 默认为1:
     -> 0 : 抛出异常,
     -> 1 : 阻塞,直到有可用链接资源
     -> 2 : 强制创建新的链接资源

    这些属性均可以在org.apache.commons.pool.impl.GenericObjectPool.Config中进行设定。

 

三.原理解析

    1) 对象池创建(参考GenericObjectPool):

  • public GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) :此方法创建一个GenericObjectPool实例,GenericObjectPool类已经实现了和对象池有关的所有核心操作,开发者可以通过继承或者封装的方式来使用它.通过此构造函数,我们能够清晰的看到,一个Pool中需要指定PoolableObjectFactory 实例,以及此对象池的Config信息.PoolableObjectFactory主要用来"创建新对象",比如当对象池中的对象不足时,可以使用PoolableObjectFactory.makeObject()方法来创建对象,并交付给Pool管理.

    此构造函数实例化了一个LinkedList作为"对象池"容器,用来存取"对象".此外还会根据timeBetweenEvictionRunsMillis的值来决定是否启动一个后台线程,此线程用来周期性扫描pool中的对象列表,已检测"对象池中的对象"空闲(idle)的时间是否达到了阀值,如果是,则移除此对象.

if ((getMinEvictableIdleTimeMillis() > 0) &&
		(idleTimeMilis > getMinEvictableIdleTimeMillis())) {
	removeObject = true;
}
...
if (removeObject) {
	try {
		_factory.destroyObject(pair.value);
	} catch(Exception e) {
		// ignored
	}
}

 

 

    2) 对象工厂PoolableObjectFactory接口:

    commons-pool通过使用ObjectFactory(工厂模式)的方式将"对象池中的对象"的创建/检测/销毁等特性解耦出来,这是一个非常良好的设计思想.此接口有一个抽象类BasePoolableObjectFactory,可供开发者继承和实现.

  • Object makeObject() : 创建一个新对象;当对象池中的对象个数不足时,将会使用此方法来"输出"一个新的"对象",并交付给对象池管理.
  • void destroyObject(Object obj) : 销毁对象,如果对象池中检测到某个"对象"idle的时间超时,或者操作者向对象池"归还对象"时检测到"对象"已经无效,那么此时将会导致"对象销毁";"销毁对象"的操作设计相差甚远,但是必须明确:当调用此方法时,"对象"的生命周期必须结束.如果object是线程,那么此时线程必须退出;如果object是socket操作,那么此时socket必须关闭;如果object是文件流操作,那么此时"数据flush"且正常关闭.
  • boolean validateObject(Object obj) : 检测对象是否"有效";Pool中不能保存无效的"对象",因此"后台检测线程"会周期性的检测Pool中"对象"的有效性,如果对象无效则会导致此对象从Pool中移除,并destroy;此外在调用者从Pool获取一个"对象"时,也会检测"对象"的有效性,确保不能讲"无效"的对象输出给调用者;当调用者使用完毕将"对象归还"到Pool时,仍然会检测对象的有效性.所谓有效性,就是此"对象"的状态是否符合预期,是否可以对调用者直接使用;如果对象是Socket,那么它的有效性就是socket的通道是否畅通/阻塞是否超时等.
  • void activateObject(Object obj) : "激活"对象,当Pool中决定移除一个对象交付给调用者时额外的"激活"操作,比如可以在activateObject方法中"重置"参数列表让调用者使用时感觉像一个"新创建"的对象一样;如果object是一个线程,可以在"激活"操作中重置"线程中断标记",或者让线程从阻塞中唤醒等;如果object是一个socket,那么可以在"激活操作"中刷新通道,或者对socket进行链接重建(假如socket意外关闭)等.
  • void void passivateObject(Object obj) : "钝化"对象,当调用者"归还对象"时,Pool将会"钝化对象";钝化的言外之意,就是此"对象"暂且需要"休息"一下.如果object是一个socket,那么可以passivateObject中清除buffer,将socket阻塞;如果object是一个线程,可以在"钝化"操作中将线程sleep或者将线程中的某个对象wait.需要注意的时,activateObject和passivateObject两个方法需要对应,避免死锁或者"对象"状态的混乱.

    3) ObjectPool接口与实现:

    对象池是commons-pool的核心接口,用来维护"对象列表"的存取;其中GenericObjectPool是其实现类,它已经实现了相关的功能.

  • Object borrowObject() : 从Pool获取一个对象,此操作将导致一个"对象"从Pool移除(脱离Pool管理),调用者可以在获得"对象"引用后即可使用,且需要在使用结束后"归还".如下为伪代码:
public Object borrowObject() throws Exception {
	Object value = null;
	synchronized (this) {
		if(!_pool.isEmpty()){
			value = _pool.remove();
		}
		
	}		
	for(;;) {     
		//如果Pool中没有"对象",则根据相应的"耗尽"策略
		if(value == null) {
			switch(whenExhaustedAction) {
				//如果耗尽,仍继续创建新"对象"
				case WHEN_EXHAUSTED_GROW:
					value = _factory.makeObject();
					break;
				//如果耗尽,则终止,此时以异常的方式退出.
				case WHEN_EXHAUSTED_FAIL:
					throw new NoSuchElementException("Pool exhausted");
				//如果耗尽,则阻塞,直到有"对象"归还
				case WHEN_EXHAUSTED_BLOCK:
					try {
						synchronized (value) {
							if (value == null) {
								//maxWait为Config中指定的"最大等待时间"
								if(maxWait <= 0) {
									latch.wait();
								} else {
										latch.wait(waitTime);
								}
							} else {
								break;
							}
						}
					} catch(InterruptedException e) {		
						//
						break;
					}
					
				default://
			}
		}

		try {
			_factory.activateObject(latch.getPair().value);
			if(_testOnBorrow &&
					!_factory.validateObject(latch.getPair().value)) {
				throw new Exception("ValidateObject failed");
			}
		   return value;
		}
		catch (Throwable e) {
			try {
				_factory.destroyObject(latch.getPair().value);
			} catch (Throwable e2) {
				//
			}
		}
	}
}

 

  • void returnObject(Object obj) : "归还"对象,当"对象"使用结束后,需要归还到Pool中,才能维持Pool中对象的数量可控,如果不归还到Pool,那么将意味着在Pool之外,将有大量的"对象"存在,那么就使用了"对象池"的意义.如下为伪代码:
public void returnObject(Object obj) throws Exception {
	try {
		boolean success = true;//
		if(_testOnReturn && !(_factory.validateObject(obj))) {
			success = false;
		} else {
			_factory.passivateObject(obj);
		}

		synchronized (this) {
			//检测pool中已经空闲的对象个数是否达到阀值.
			if((_maxIdle >= 0) && (_pool.size() >= _maxIdle)) {
				success = false;
			} else if(success) {
				_pool.addFirst(new ObjectTimestampPair(obj));
			}
		}

		// Destroy the instance if necessary
		if(!success) {
			try {
				_factory.destroyObject(obj);
			} catch(Exception e) {
				// ignored
			}
		}
	} catch (Exception e) {
		//
	}
}
  • void invalidateObject(Object obj) : 销毁对象,直接调用ObjectFactory.destroyObject(obj);.
  • void addObject() : 开发者可以直接调用addObject方法用于直接创建一个"对象"并添加到Pool中.

四.代码实例.

    本实例主要用来演示一个"TCP连接池".

    1) ConnectionPoolFactory.java:

import org.apache.commons.pool.BasePoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool.Config;

public class ConnectionPoolFactory {

	private GenericObjectPool pool;

	public ConnectionPoolFactory(Config config,String ip,int port){
		ConnectionFactory factory = new ConnectionFactory(ip, port);
		pool = new GenericObjectPool(factory, config);
	}
	
	public Socket getConnection() throws Exception{
		return (Socket)pool.borrowObject();
	}
	
	public void releaseConnection(Socket socket){
		try{
			pool.returnObject(socket);
		}catch(Exception e){
			if(socket != null){
				try{
					socket.close();
				}catch(Exception ex){
					//
				}
			}
		}
	}
	
	/**
	 * inner 
	 * @author qing
	 *
	 */
	class ConnectionFactory extends BasePoolableObjectFactory {

		private InetSocketAddress address;
		
		public ConnectionFactory(String ip,int port){
			address = new InetSocketAddress(ip, port);
		}
		
		@Override
		public Object makeObject() throws Exception {
			Socket socket = new Socket();
			socket.connect(address);
			return socket;
		}
		
		public void destroyObject(Object obj) throws Exception  {
			if(obj instanceof Socket){
				((Socket)obj).close();
			}
	    }

	    public boolean validateObject(Object obj) {
	    	if(obj instanceof Socket){
	    		Socket socket = ((Socket)obj);
				if(!socket.isConnected()){
					return false;
				}
				if(socket.isClosed()){
					return false;
				}
				return true;
			}
	    	return false;
	    }


	}
	
}

 

    2) TestMain.java(测试类):

public class TestMain {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Config config = new Config();
		config.maxActive = 16;
		config.maxWait = 30000;
		ConnectionPoolFactory poolFactory = new ConnectionPoolFactory(config, "127.0.0.1", 8011);
		Socket socket = null ;
		try{
			socket = poolFactory.getConnection();
			////
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(socket != null){
				poolFactory.releaseConnection(socket);
			}
		}

	}

}

 

 

---END--

  • 大小: 23.3 KB
分享到:
评论
2 楼 memoryisking 2015-03-05  
写的不错,内容详细,这里也有很多关于缓存的内容:
http://swiftlet.net/archives/category/original-content/original-content-cache
1 楼 flashing 2014-05-27  
Nice example!

相关推荐

    Apache commons-pool2-2.4.2源码学习笔记

    Apache Commons Pool2是Java开发中的一个对象池库,主要用于提高对象的重用效率,从而提升系统性能。在本文中,我们将深入探讨这个库的核心概念、设计模式以及如何通过源码学习来理解其工作原理。 首先,Apache ...

    commons-pool-1.2-src.zip

    Apache Commons Pool 是一个开源项目,提供了对象池化的实现,它是一个通用的对象池库,用于创建各种资源对象的池,如数据库连接池。标题中的"commons-pool-1.2-src.zip"表明这是一个包含Apache Commons Pool 1.2...

    commons-pool-1.5.4-src.tar.gz

    源代码分析对于深入理解 Apache Commons Pool 的工作原理和优化点至关重要。通过对 `commons-pool-1.5.4-src` 的解压和阅读,开发者可以研究其实现细节,如对象池的内部数据结构、调度算法,以及如何通过配置参数...

    commons-pool2-2.4.2-src

    在"commons-pool2-2.4.2-src"源码包中,我们可以深入理解Apache Commons Pool2的工作原理和实现细节。源码分析有助于开发者掌握如何设计和使用高效的对象池,并且可以定制自己的对象池策略。 1. **核心接口和类**:...

    commons-pool1.5.2 jar包+源码

    Apache Commons Pool 是一个Java对象池库,用于管理可重用对象。这个库提供了一种机制,使得应用程序可以高效地重复使用已经创建的对象,避免了频繁创建和销毁对象带来的性能开销。1.5.2是该库的一个版本,它包含了...

    commoms-pool-1.5.4-sources.jar.zip

    Apache Commons Pool是一个开源的、高度可配置的对象池库,广泛应用于Java编程中。它为开发者提供了对象池化的基础设施,帮助开发者有效地管理资源,避免频繁创建和销毁对象带来的性能开销。在本篇文章中,我们将...

    commons-pool2-sr:Apache Commons Pool原始代码剖析笔记

    Apache Commons Pool2是一个Java对象池库,用于管理可重用对象。对象池是软件设计中的一个概念,通过池化资源,可以有效地减少创建和销毁对象的开销,提高系统的性能和效率。Apache Commons Pool2是Apache Commons...

    commons-dbcp源码

    - `org.apache.commons.pool`:这是Apache的通用对象池库,DBCP利用它来管理数据库连接。 - `org.apache.commons.dbcp.cpdsadapter`:提供了对连接池数据源适配器的支持。 - `org.apache.commons.dbcp.proxy`:...

    commons-dbcp-1.3

    Apache Commons DBCP(Database Connection Pool)是Apache软件基金会开发的一个开源项目,它提供了一个数据库连接池组件。在Java应用程序中,数据库连接的创建和销毁是相对耗时的操作,而DBCP通过池化技术实现了...

    commons-dbcp-1.2.2源码

    Apache Commons DBCP(数据库连接池)是Java开发中常用的一个数据库连接池组件,它是在Apache Commons项目下的一份开源库。源码分析对于理解其工作原理和优化应用至关重要。在"commons-dbcp-1.2.2源码"中,我们可以...

    commons-dbcp-1.3-src.zip

    Apache Commons DBCP,全称为"Database Connection Pool",是Apache软件基金会开发的一个开源数据库连接池组件。在Java应用程序中,DBCP作为一个高效、稳定、成熟的数据库连接池解决方案,广泛应用于各种项目中,以...

    commons-dbcp2-2.0.1-src

    《Apache Commons DBCP2 2.0.1 源码分析》 Apache Commons DBCP2(DBCP第二版)是Apache软件基金会提供的一款开源数据库连接池组件,主要用于管理数据库连接,提高应用程序的效率和性能。在版本2.0.1中,DBCP2已经...

    commons-dbcp-1.4源码

    Apache Commons DBCP(Database Connection Pool)是Apache软件基金会提供的一款开源数据库连接池组件,版本1.4是我们这里关注的重点。数据库连接池在Java应用程序中扮演着至关重要的角色,它提高了应用性能,优化了...

    dbcp2-2.1.1和pool2-2.4.2 jar包

    接下来,Commons Pool2是Apache Commons项目中的对象池库,它是DBCP2依赖的连接池基础组件。Pool2-2.4.2是这个库的版本,它提供了通用的对象池服务,不仅适用于数据库连接,还可以用于任何需要对象复用的场景。...

    数据库连接池驱动程序包

    DBCP(Jakarta Database Connection Pool)是Apache组织提供的一个开源JDBC连接池组件,它是Commons-pool对象池和Commons-dbcp数据源的结合。DBCP的特点如下: - 基于Apache Commons的池化组件,性能相对稳定。 - ...

    DBCP连接池原理分析

    ### DBCP连接池原理分析 #### DBCP概述与版本区分 DBCP(Database Connection Pool)是由Apache组织提供的一款开源连接池实现。它能够帮助应用程序有效地管理与数据库的连接资源,通过重用预创建的数据库连接来...

    java 四种连接池测试代码(和博文配套)

    3. **DBCP (Apache Commons DBCP)**:Apache Commons DBCP是Apache组织提供的一个基于Jakarta-Commons-Pool对象池API的数据库连接池。它简单易用,但相比其他现代连接池,性能可能稍逊一筹。测试代码会展示如何设置...

    Db-Pool-Performace-Comparison:各种DB Pool的性能测试

    4. **Apache DBCP**:基于Jakarta POOL库,是Apache Commons的一个组件。虽然稳定,但相对于其他连接池,性能较低,且不支持最新的JDBC规范。 在"Db-Pool-Performance-Comparison"项目中,通常会通过执行一系列的...

    数据库连接池

    - **DBCP**:Apache基础软件基金会的项目,基于Jakarta-Commons-Pool和Jakarta-Commons-Dbcp。 - **HikariCP**:高性能的连接池,设计目标是提供最小的延迟和最高的并发性能。 - **Druid**:阿里巴巴开源的数据库...

Global site tag (gtag.js) - Google Analytics