转载于:http://blog.sina.com.cn/s/blog_616e189f0101b12f.html
Apache Commons pool用于提供对象池或者连接池的编写工具,本文简要介绍一下commons pool的主要功能,并以jedis中如何使用commons pool作为例子,加深理解。
根据GenericObjectPool api描述,池的配置如下:
(1)maxActive :表示能够分配的最大对象数目(包括正在被使用的和空闲等待的对象)。为负数时表示对象数目没有上限,默认值为8,当pool维护的对象数目超过maxActive配置的数目,pool的资源被耗尽。
(2)maxIdle:在同一时刻内最多可以维护的空闲对象的数目,默认为8.
(3)whenExhaustedAction:当调用borrow,pool维护的对象超过maxActive时,通过配置将会出现如下动作:
1)WHEN_EXHAUSTED_FAIL borrowObject()将会抛出NoSuchElementException;
2) WHEN_EXHAUSTED_GROW borrowObject()将会继续创建新的对象,并返回,因此,pool维护的对像数将超出maxActive;
3)WHEN_EXHAUSTED_BLOCK,borrowObject()将会阻塞,直到有可用新的或者空闲的object为止,或者如果配置了maxWait,如果请求阻塞超时,将抛出NoSuchElementException. 如果maxWait为负数,请求将会无限制的阻塞下去,默认配置。
(4)testOnBorrow 如果设置,在调用borrowObject方法时,测试对象的可用性(使用PoolableObjectFactory.validateObject(T)方法),如果测试失败,此Object将会从池中删除,取得下一个Object进行测试。默认设置为false(对于链接池,测试对象往往会增加一次网络访问)。
(5)testOnReturn,在returnObject(T)时,会测试对象可用性,与testOnBorrow相似。
对于idle的object,可以设置一定的驱逐策略,让池中尽量保证少的维护对象。需要注意的是,过于频繁的测试驱逐很可能会引发性能问题。配置方法如下:
(1)timeBetweenEvictionRunsMillis 定义驱逐扫描的时间间隔。配置为负值,不需要执行驱逐线程,默认为-1;
(2)minEvictableIdleTimeMillis 确认某个Object空闲超时时间,负值为永远不超时,在驱逐线程清理超时对象时,只有空闲时间超过minEvictableIdleTimeMillis的对象才会被清除,默认为30分钟;
(3)testWhileIdle 对idle的Object进行验证,无效的Object将会从池中删除;
(4)softMinEvictableIdleTimeMillis 在驱逐时,可以预留一定数量的idle 对象,是minEvictableIdleTimeMillis的一个附加条件,如果为负值,表示如果驱逐,则所有idle对象都被驱逐;
(5)numTestsPerEvictionRun 一次驱逐过程中,最多驱逐对象的个数;
对象队列有两种,一种是LIFO(last in first out)和FIFO(First in first out),可以选择两种之一。(注:对象在pool中实际是以队列的形式存在的,按照一定策略,在对象借出或者归还时插入队列的不同位置)
Lifo用于设置策略,默认为true。
下面,我们从jedis中拿出池代码,来看看池是如何使用的。
1. 池factory(JedisPool.java)
private static class JedisFactory extends BasePoolableObjectFactory {
private final String host;
private final int port;
private final int timeout;
private final String password;
private final int database;
public JedisFactory(final String host, final int port,
final int timeout, final String password, final int database) {
super();
this.host = host;
this.port = port;
this.timeout = timeout;
this.password = password;
this.database = database;
}
public Object makeObject() throws Exception {
final Jedis jedis = new Jedis(this.host, this.port, this.timeout);
jedis.connect();
if (null != this.password) {
jedis.auth(this.password);
}
if( database != 0 ) {
jedis.select(database);
}
return jedis;
}
public void destroyObject(final Object obj) throws Exception {
if (obj instanceof Jedis) {
final Jedis jedis = (Jedis) obj;
if (jedis.isConnected()) {
try {
try {
jedis.quit();
} catch (Exception e) {
}
jedis.disconnect();
} catch (Exception e) {
}
}
}
}
public boolean validateObject(final Object obj) {
if (obj instanceof Jedis) {
final Jedis jedis = (Jedis) obj;
try {
return jedis.isConnected() && jedis.ping().equals("PONG");
} catch (final Exception e) {
return false;
}
} else {
return false;
}
}
}
}
对象池工厂,继承与BasePoolableObjectFactory,并重写相应方法。
2. 池的实现
在jedis的Pool.java中,有如下代码:
public Pool(finalGenericObjectPool.Config poolConfig,
PoolableObjectFactory factory) {
this.internalPool = new GenericObjectPool(factory, poolConfig);
}
@SuppressWarnings("unchecked")
public T getResource() {
try {
return (T) internalPool.borrowObject();
} catch (Exception e) {
throw new JedisConnectionException(
"Could not get a resource from the pool", e);
}
}
public void returnResourceObject(final Object resource) {
try {
internalPool.returnObject(resource);
} catch (Exception e) {
throw new JedisException(
"Could not return the resource to the pool", e);
}
}
3. 我们使用如下方法,创建池,获取池资源,返回池资源
Config config = new Config();
config.maxActive = 20;
JedisPool pooles = new JedisPool(config, "10.4.21.51", 6379);
Jedis jedis = pooles.getResource();
List rets = jedis.lrange("999", 1000000000000999990L, 1000);
pooles.returnResource(jedis);
4. 小结
GenericObjectPool的构造函数有两个参数,一个是Config,一个是PoolableObjectFactory,其中Config用于配置我们前面提到的参数选项,而PoolableObjectFactory则是实际创建对象,验证对象,销毁对象等的实际操作。
在我们的例子中,config仅设置了maxActive,即最大的可用Object数目,每创建一个Object,jedis就会创建一个与redis的链接,因此控制maxActive是必要的;当调用borrowObject()资源耗尽时,请求会排队并且maxwait值为-1,表示永远等待下去;timeBetweenEvictionRunsMillis默认为-1,因此,没有驱逐策略;lifo默认为true,因此使用last in first out 策略。