commons pool2
前言
项目中使用到了微服务的框架thrift,服务间通过RPC通讯,每次RPC调用,都需要建立长连接,使用完成后关闭长连接,在高并发场景下,效率不高,这时,我们就需要采用池化技术,使用连接池,通过空间换时间,不必每次都创建和关闭连接,提高我们的RPC的调用效率。这里我们采用的是Apache的commons pool2,这时一个开源的对象池组件,使用较为广泛,我们常用的jedis就是使用它作为底层的对象池。
使用
commons pool2可以通过maven方式引入,也可以通过jar包的方式引入,具体根据项目情况定。
如何在项目中实际使用呢?需要以下3个步骤:
- 封装API,引用GenericObjectPool的接口
- 实现 PooledObjectFactory接口,实现对象的生成,校验和销毁
- 实现GenericObjectPoolConfig配置,我们采用的方式是继承GenericObjectPoolConfig,做好对象池的配置,包括对象池的大小,容量,空闲检测策略,借用和归还策略等
针对对象池的配置,这里列举几个关键配置:
- maxTotal 对象池的容量
- maxIdle 对象池最大的空闲对象数量
- maxWaitMillis 获取对象的最大等待时间
- testWhileIdle 是否开启空闲检测
- minEvictableIdleTimeMillis 连接空闲的最小时间,达到此值后空闲连接将可能会被移除。负值(-1)表示不移除
- timeBetweenEvictionRunsMillis 空闲连接,检测线程,检测的周期
- numTestsPerEvictionRun 检测线程每次检测的对象数量
- testOnBorrow 获取对象时是否校验对象有效性
- testOnReturn 归还对象时是否校验对象有效性
commons pool2的关健类
GenericObjectPool是我们接下来会使用到的对象池底层的核心类,类图见下:
GenericObjectPool类里面函数较多,我们不一一列举,这里列出几个关健的函数,便于后续使用:
public T borrowObject(long borrowMaxWaitMillis) 获取对象
public void returnObject(T obj) 归还对象
public void invalidateObject(T obj) 发生异常归还的处理
public void close() 销毁对象池
public void evict() 空闲检测函数
borrowObject 借用对象
private final AtomicLong createCount = new AtomicLong(0); //创建对象的数量
private final LinkedBlockingDeque<PooledObject<T>> idleObjects;//空闲对象队列
public T borrowObject(long borrowMaxWaitMillis) throws Exception {
assertOpen();
AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
(getNumIdle() < 2) &&
(getNumActive() > getMaxTotal() - 3) ) {
removeAbandoned(ac);
}
PooledObject<T> p = null;
// Get local copy of current config so it is consistent for entire
// method execution
boolean blockWhenExhausted = getBlockWhenExhausted();
boolean create;
long waitTime = System.currentTimeMillis();
//对象借用的核心流程
while (p == null) {
create = false;
//资源耗尽是否阻塞,默认为true
if (blockWhenExhausted) {
//尝试从队列中获取对象,从头获取
p = idleObjects.pollFirst();
if (p == null) {
//获取失败,尝试创建对象
p = create();
if (p != null) {
create = true;
}
}
if (p == null) {
//如果创建对象失败,表明对象池已经达到了maxTotal,无法创建对象
//borrowMaxWaitMillis为获取对象最大等待时间,如果为-1,则从队列头尝试获取
if (borrowMaxWaitMillis < 0) {
p = idleObjects.takeFirst();
} else {
//从队列头获取,如果队列为空,则会阻塞等待borrowMaxWaitMillis
p = idleObjects.pollFirst(borrowMaxWaitMillis,
TimeUnit.MILLISECONDS);
}
}
//此时还没有获取到,表明没有人释放对象,等待超时,或者对端服务已经挂掉,抛出异常
if (p == null) {
throw new NoSuchElementException(
"Timeout waiting for idle object");
}
//设置对象的状态,由IDLE设置为ALLOCATED,同时设置对象的属性,如lastBorrowTime,lastUseTime等
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;
}
}
//对象获取成功,这时调用我们自己编写的factory对象,对对象做激活,校验
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())) {
//如果设置了借用时,校验对象,则调用factory.validateObject校验对象
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;
}
}
}
}
}
//统计对象池的使用情况
updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
//返回获取的对象
return p.getObject();
}
returnObject 归还对象
private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
new ConcurrentHashMap<IdentityWrapper<T>, PooledObject<T>>();//用于统计对象的map
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) {
//判断对象的状态,如果为ALLOCATED表明已经被归还了,抛出异常
final PooledObjectState state = p.getState();
if (state != PooledObjectState.ALLOCATED) {
throw new IllegalStateException(
"Object has already been returned to this pool or is invalid");
} else {
//对象状态更改为RETURNING
p.markReturning(); // Keep from being marked abandoned
}
}
long activeTime = p.getActiveTimeMillis();
//归还时是否校验对象
if (getTestOnReturn()) {
if (!factory.validateObject(p)) {
try {
destroy(p);
} 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);
}
//统计returnedCount和activeTimes
updateStatsReturn(activeTime);
return;
}
//将对象状态更改为:IDLE
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()) {
//如果超过对象池最大空闲数量,则将当前归还的p销毁
try {
destroy(p);
} catch (Exception e) {
swallowException(e);
}
} else {
//归还,将对象重新如队列,具体的入队列方式,根据实际设置的队列存取,true放到队列头部,false放到队列尾部
if (getLifo()) {
idleObjects.addFirst(p);
} else {
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);
}
对象的状态
属性 | 描述 |
---|---|
IDLE | 空闲 |
ALLOCATED | 使用 |
EVICTION | 在队列中,有效 |
EVICTION_RETURN_TO_HEAD | 不在队列中,当前正在被检测,需要时会被归还到队头 |
VALIDATION | 在队列中,有效 |
VALIDATION_PREALLOCATED | 不在队列中,对象被借用,正在验证 |
VALIDATION_RETURN_TO_HEAD | 不在队列中,对象在验证,校验完成会被放到队列头部 |
INVALID | 非法 |
ABANDONED | 抛弃 |
RETURNING | 归还 |
结语
第一版我们使用的是commons-pool-1.6版本,后面升级为2.4.2版本,两个版本在对象池的管理上,2.4.2版本更胜一筹,队列中放置的不再单单是对象,而是对象的封装PooledObject,里面封装了对象的一些状态,便于后续进一步的对象管理。