对于那些创建耗时较长,或者资源占用较多的对象,比如网络连接,线程之类的资源,通常使用池化来管理这些对象,从而达到提高性能的目的。比如数据库连接池(c3p0, dbcp), java的线程池 ExecutorService. Apache Commons Pool提供一套池化规范接口,以及实现通用逻辑,我们只需要要实现其抽象出来的方法就可以。Commons Pool主要有以下几个对象
  PooledObject:这个就是前面所说需要池化的资源,被池化的对象可以抽离出共有属性,如,创建时间,状态,最近一次使用时间等
  PooledObjectFactory: 对象工厂,负责对PooledOjbect的创建,状态验证,销毁之类的工作
  ObjectPool: 对象池,它是负责和对象使用者直接打交道的, 对使用者提供获取对象,返还对象接口
  英文不太好的同学可能被这几个对象的命名搞晕,其实世间万物的道理都是相通的。如果把图书馆的书比作PooledObject 的话,那么图书馆就是ObjectPool,图书馆为了管理书,对书添加了入库时间,书借存状态等用于管理的属性。图书馆(ObjectPool)对借书人提供借书,还书的服务(即接口)。而书(PooledObject )的印刷,质量检验,回收(有点不现实)等实实在在的工作还是得交给印刷厂(PooledObjectFactory )来做。其流程关系如下:
  下面看看如何使用Commons Pool如何实现自己的对象池。创建自己的对象池大致需要以下工作,
  1. 首先你已经编写好了你的资源对象(这部分不属于池化内容),之后编写实现apache的PooledObjectFactory<T>接口的Factory类,这是编写自己对象池最主要的工作。你的Factory可能需要添加一些用于池化对象的初始化 ,池化对象的验证等参数作为成员变量。
  2. 编写自己的Pool类,让其继承或者内部引用apache的GenericObjectPool<T>,GenericObjectPool实现了ObjectPool接口,已经封装了对池化对象的生命周期管理逻辑
  3. 可选部分,继承apache的GenericObjectPoolConfig,重写构造器,添加一些适合自己业务场景的初始化参数。
  我们以Jedis的源码为例,学习它的实现。我们先看下使用JedisPool操作Redis的简单例子
package com.eg.test.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class TestPool {
public static void main(String[] args) {
//JedisPoolConfig继承apache的GenericObjectPoolConfig,配置Pool的相关参数如下:
JedisPoolConfig config = new JedisPoolConfig();
//如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
config.setMaxTotal(500);
//控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
config.setMaxIdle(5);
//表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
config.setMaxWaitMillis(30000);;
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
config.setTestOnBorrow(true); JedisPool pool = new JedisPool(config, "192.168.2.191", 8888);
//从pool中获取对象
Jedis jedis = pool.getResource();
String value = jedis.get("someKey"); }
}

  首先看JedisFactory的实现:

class JedisFactory implements PooledObjectFactory<Jedis> {
private final AtomicReference<HostAndPort> hostAndPort = new AtomicReference<HostAndPort>();
private final int connectionTimeout;
private final int soTimeout; //省略构造函数,都是一些初始化成员变量的操作 @Override
public void activateObject(PooledObject<Jedis> pooledJedis) throws Exception {
final BinaryJedis jedis = pooledJedis.getObject();
if (jedis.getDB() != database) {
jedis.select(database);
}
} @Override
public void destroyObject(PooledObject<Jedis> pooledJedis) throws Exception {
final BinaryJedis jedis = pooledJedis.getObject();
if (jedis.isConnected()) {
try {
try {
jedis.quit();
} catch (Exception e) {
}
jedis.disconnect();
} catch (Exception e) {
}
}
} @Override
public PooledObject<Jedis> makeObject() throws Exception {
final HostAndPort hostAndPort = this.hostAndPort.get();
final Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout, soTimeout, ssl,
sslSocketFactory, sslParameters, hostnameVerifier);
try {
jedis.connect();
if (null != this.password) {
jedis.auth(this.password);
}
if (database != 0) {
jedis.select(database);
}
if (clientName != null) {
jedis.clientSetname(clientName);
}
} catch (JedisException je) {
jedis.close();
throw je;
}
return new DefaultPooledObject<Jedis>(jedis);
}
@Override
public void passivateObject(PooledObject<Jedis> pooledJedis) throws Exception {
// TODO maybe should select db 0? Not sure right now.
}
@Override
public boolean validateObject(PooledObject<Jedis> pooledJedis) {
final BinaryJedis jedis = pooledJedis.getObject();
try {
HostAndPort hostAndPort = this.hostAndPort.get();
String connectionHost = jedis.getClient().getHost();
int connectionPort = jedis.getClient().getPort();
return hostAndPort.getHost().equals(connectionHost) && hostAndPort.getPort() == connectionPort
&& jedis.isConnected() && jedis.ping().equals("PONG");
} catch (final Exception e) {
return false;
}
}
}

  我们看到JedisFactory代码较少,但是逻辑很清晰。该Factory将作为ObjectPool的成员变量,其中四个重写的方法被ObjectPool管理对象生命周期的时候调用。makeobject()方法负责创建Jedis实例,成功调用connect()方法建立有状态的socket连接之后,返回一个包装了jedis的DefaultPooledObject对象,DefaultPooledObject实现了关于统计池化对象状态信息的PooledObject接口。validateObject()方法用于对对象状态的检验,Jedis对象的状态通过socket的ping-pong来验证连接是否正常。destroyObject()方法用来销毁对象,Jedis对象将会断开连接,回收资源。

  再看JedisPool的实现,由于JedisPool继承Pool<T>,所以我们主要看Pool<T>的部分代码:

public abstract class Pool<T> implements Closeable {
protected GenericObjectPool<T> internalPool; public Pool(final GenericObjectPoolConfig poolConfig, PooledObjectFactory<T> factory) {
initPool(poolConfig, factory);
} public boolean isClosed() {
return this.internalPool.isClosed();
}
public void initPool(final GenericObjectPoolConfig poolConfig, PooledObjectFactory<T> factory) {
if (this.internalPool != null) {
try {
closeInternalPool();
} catch (Exception e) {
}
}
this.internalPool = new GenericObjectPool<T>(factory, poolConfig);
}
public T getResource() {
try {
return internalPool.borrowObject();
} catch (NoSuchElementException nse) {
throw new JedisException("Could not get a resource from the pool", nse);
} catch (Exception e) {
throw new JedisConnectionException("Could not get a resource from the pool", e);
}
}
protected void returnResourceObject(final T resource) {
if (resource == null) {
return;
}
try {
internalPool.returnObject(resource);
} catch (Exception e) {
throw new JedisException("Could not return the resource to the pool", e);
}
} public void addObjects(int count) {
try {
for (int i = 0; i < count; i++) {
this.internalPool.addObject();
}
} catch (Exception e) {
throw new JedisException("Error trying to add idle objects", e);
}
}
}
  JedisPool通过内部引用GenericObjectPool,包装其接口的装饰者模式,相比继承来说这种模式更加灵活。JedisPool的构造方法需要将JedisFactory以及JedisPoolConfig创建标准的ObjectPool作为自己的成员变量。所以pool.getResource()方法的背后还是调用PoolObject.borrowObject()。
 
  最后我们稍微看下JedisPoolConfig,只是做了一些预初始化参数的工作。
public class JedisPoolConfig extends GenericObjectPoolConfig {
public JedisPoolConfig() {
// defaults to make your life with connection pool easier :)
setTestWhileIdle(true);
setMinEvictableIdleTimeMillis(60000);
setTimeBetweenEvictionRunsMillis(30000);
setNumTestsPerEvictionRun(-1);
}
}

池化 - Apache Commons Pool的更多相关文章

  1. 对象池化技术 org.apache.commons.pool

    恰当地使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率.Jakarta Commons Pool组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现,可以 ...

  2. JedisCluster中应用的Apache Commons Pool对象池技术

    对象池技术在服务器开发上应用广泛.在各种对象池的实现中,尤其以数据库的连接池最为明显,可以说是每个服务器必须实现的部分.   apache common pool 官方文档可以参考:https://c ...

  3. Apache Commons Pool 故事一则

    Apache Commons Pool 故事一则 最近工作中遇到一个由于对commons-pool的使用不当而引发的问题,习得正确的使用姿势后,写下这个简单的故事,帮助理解Apache Commons ...

  4. Apache Commons Pool 故事一则 专题

    Apache Commons Pool 故事一则 最近工作中遇到一个由于对commons-pool的使用不当而引发的问题,习得正确的使用姿势后,写下这个简单的故事,帮助理解Apache Commons ...

  5. Tomcat 开发web项目报Illegal access: this web application instance has been stopped already. Could not load [org.apache.commons.pool.impl.CursorableLinkedList$Cursor]. 错误

    开发Java web项目,在tomcat运行后报如下错误: Illegal access: this web application instance has been stopped already ...

  6. NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool

    错误:Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/pool/impl ...

  7. Spring + Tomcat 启动报错java.lang.ClassNotFoundException: org.apache.commons.pool.impl.GenericObjectPool

    错误如下: -- ::,-[TS] INFO http-- org.springframework.beans.factory.support.DefaultListableBeanFactory - ...

  8. org/apache/commons/pool/impl/GenericObjectPool异常的解决办法

    org/apache/commons/pool/impl/GenericObjectPool异常的解决办法 webwork+spring+hibernate框架的集成, 一启动Tomcat服务器就出了 ...

  9. Cache Lucene IndexReader with Apache Commons Pool

    IndexReaderFactory.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 2 ...

随机推荐

  1. Pull生成&解析

    开篇注意,由于解析有可能有大文件非常耗时,建议另开一个线程解析也可以不开具体视情况而定     Pull生成 1.通过xml获得序列化的实例 XmlSerializer nxs = Xml.newSe ...

  2. C#中DataGridView控件使用大全

    DataGridView 动态添加新行: DataGridView控件在实际应用中非常实用,特别需要表格显示数据时.可以静态绑定数据源,这样就自动为DataGridView控件添加相应的行.假如需要动 ...

  3. oracle导出数据显示出现ora-00109或者LRM-00109出错修改办法

    出现这种问题是因为日期格式的问题,调整日期格式后就可以了 改成yyyy-mm-dd的格式就好了

  4. css基础-背景文本

    css背景 1. background-color:#6495ed; 2. background-image:url('bgdesert.jpg'); 3. background-repeat:rep ...

  5. 创建对象时引用的关键字,assign,copy,retain

    创建对象时引用的关键字:assign: 简单赋值,不更改索引计数(强引用)copy: 建立一个索引计数为1的对象,然后释放旧对象retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索 ...

  6. js跨浏览器事件处理

    var EventUtil = { addHandler: function(element,type,handler){ if(element.addEventListener){ element. ...

  7. PHP使用curl伪造IP地址和header信息

    curl虽然功能强大,但是只能伪造$_SERVER["HTTP_X_FORWARDED_FOR"],对于大多数IP地址检测程序来说,$_SERVER["REMOTE_AD ...

  8. js 判断js函数、变量是否存在 JS保存和删除cookie操作,判断cookie是否存在的方法

    //是否存在指定函数 function isExitsFunction(funcName) {    try {        if (typeof(eval(funcName)) == " ...

  9. USB系列之二:读取USB设备的描述符

    在前面的文章中,我们已经给出了USB协议的链接地址,从这篇文章起,我们会涉及到许多USB 1.1的内容,我们的指导思想是先从熟悉USB 1.1协议入手,先使用现成的HCD和USBD,直接面对客户端驱动 ...

  10. Qt中一些常用的格式转换

    转自:http://blog.csdn.NET/yh_1988/article/details/7190356 用Qt经常头痛于一些格式不能通用的问题 在此记录备用 1 (20120112)QStri ...