文章摘要(AI生成)
对象连接池属性主要封装在BaseGenericObjectPool中,包括maxTotal、blockWhenExhausted、maxWaitDuration、lifo等默认配置属性。其中maxTotal表示最大连接数,blockWhenExhausted表示当连接用尽时是否阻塞等待,maxWaitDuration表示最大等待时间,lifo表示是否采用后进先出等策略。除此之外,还包括fairness、testOnCreate、testOnBorrow、testOnReturn、testWhileIdle、durationBetweenEvictionRuns、numTestsPerEvictionRun等属性。这些属性可根据具体需求进行配置,以提高连接池的性能和稳定性。
对象连接池属性主要封装在BaseGenericObjectPool
中(GenericObjectPool
的父类),对应的默认配置如下:
// Configuration attributes
private volatile int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
private volatile boolean blockWhenExhausted = BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED;
private volatile Duration maxWaitDuration = BaseObjectPoolConfig.DEFAULT_MAX_WAIT;
private volatile boolean lifo = BaseObjectPoolConfig.DEFAULT_LIFO;
private final boolean fairness;
private volatile boolean testOnCreate = BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE;
private volatile boolean testOnBorrow = BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW;
private volatile boolean testOnReturn = BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN;
private volatile boolean testWhileIdle = BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE;
private volatile Duration durationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS;
private volatile int numTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
private volatile Duration minEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION;
private volatile Duration softMinEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION;
private volatile EvictionPolicy<T> evictionPolicy;
private volatile Duration evictorShutdownTimeoutDuration = BaseObjectPoolConfig.DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT;
对象连接池创建属性
maxTotal-连接池最大数量
maxTotal
属性用于线程池初始化创建时使用。具体用法在连接池创建的方法中GenericObjectPool.create()
,在连接池创建时已经讲过,不再赘述。
blockWhenExhausted-是否等待获取连接
blockWhenExhausted
连接耗尽时是否等待获取连接,如果阻塞为true则在连接池耗尽时,根据我们设置的maxWaitDuration
来尝试获取连接;如果为false则直接抛出异常,也是在连接池创建时使用。
maxWaitDuration-最大等待时间
和blockWhenExhausted
配合使用,当连接池连接耗尽时,是否等待maxWaitDuration
再取获取连接。
需要注意的是,该属性值默认为-1,而代码里有这样的判断:
//borrowObject()
if (borrowMaxWaitDuration.isNegative()) {
p = idleObjects.takeFirst();
} else {
p = idleObjects.pollFirst(borrowMaxWaitDuration);
}
//borrowMaxWaitDuration.isNegative()
public boolean isNegative() {
return seconds < 0;
}
也就是虽然我们设置了blockWhenExhausted
为true,如果没设置等待时间maxWaitDuration
,取连接时同样不会等待。
对象连接池使用属性
由于我们连接数据库时,mysql会给client端指定每个数据库连接的最大空闲等待时长,如果我们没有开启空闲线程管理的话(下面会讲),就需要在创建、使用时验证获取到的空闲连接的可用性。
testOnBorrow-使用前是否检测连接可用性
这个设置用来在我们执行真正的sql前,检测连接是否可用。默认不开启。
testOnCreate-创建连接后检测连接可用性
这个设置用在我们初始化连接池创建连接后,检测连接是否可用,默认不开启
空闲线程管理
讲解其他属性时,我们首先要了解不再使用的连接是如何回收的,线程池对连接的管理和我们其他业务一样,也是通过定时任务进行管理回收的,那么这个定时任务是如何开启的呢?
durationBetweenEvictionRuns-指定时间创建Evictor
durationBetweenEvictionRuns为空闲连接管理的线程执行的时间间隔
这个属性在设置时的代码如下:
public final void setTimeBetweenEvictionRuns(final Duration timeBetweenEvictionRuns) {
this.durationBetweenEvictionRuns = PoolImplUtils.nonNull(timeBetweenEvictionRuns, BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS);
startEvictor(this.durationBetweenEvictionRuns);
}
而在中启动销毁器的方法中,方法通过添加定时延迟任务实现了定时监控空闲连接并进行销毁的逻辑,这里定时任务的实现没有使用传统的springschedule等庞大的定时任务框架,而是使用了java自带的定时线程池ScheduledThreadPoolExecutor
实现,这里不具体展开,有兴趣的同学可以自行百度了解。由定时线程池的创建参数可知,这里创建了只有一个定时线程的线程池,而定时线程执行的时间间隔则是我们设置的durationBetweenEvictionRuns
final void startEvictor(final Duration delay) {
synchronized (evictionLock) {
final boolean isPositiverDelay = PoolImplUtils.isPositive(delay);//判断设置的间隔时间不为0,-1,null等无效值
if (evictor == null) {
if (isPositiverDelay) {
evictor = new Evictor();
EvictionTimer.schedule(evictor, delay, delay);
}
} else if (isPositiverDelay) {
synchronized (EvictionTimer.class) {
EvictionTimer.cancel(evictor, evictorShutdownTimeoutDuration, true);
evictor = null;
evictionIterator = null;
evictor = new Evictor();
EvictionTimer.schedule(evictor, delay, delay);
}
} else { // Stopping evictor
EvictionTimer.cancel(evictor, evictorShutdownTimeoutDuration, false);
}
}
}
//EvictionTimer.schedule(evictor, delay, delay)
static synchronized void schedule(
final BaseGenericObjectPool<?>.Evictor task, final Duration delay, final Duration period) {
if (null == executor) {
executor = new ScheduledThreadPoolExecutor(1, new EvictorThreadFactory());
executor.setRemoveOnCancelPolicy(true);
executor.scheduleAtFixedRate(new Reaper(), delay.toMillis(), period.toMillis(), TimeUnit.MILLISECONDS);
}
final WeakReference<Runnable> ref = new WeakReference<>(task);
final WeakRunner runner = new WeakRunner(ref);
final ScheduledFuture<?> scheduledFuture = executor.scheduleWithFixedDelay(runner, delay.toMillis(),
period.toMillis(), TimeUnit.MILLISECONDS);
task.setScheduledFuture(scheduledFuture);
taskMap.put(ref, runner);
}
Evictor
则是我们负责销毁空闲连接的守护线程,它的具体实现为:
public void run() {
final ClassLoader savedClassLoader =
Thread.currentThread().getContextClassLoader();
try {
...
try {
evict();
} catch(final Exception e) {
swallowException(e);
} catch(final OutOfMemoryError oome) {
oome.printStackTrace(System.err);
}
try {
ensureMinIdle();
} catch (final Exception e) {
swallowException(e);
}
} finally {
// Restore the previous CCL
Thread.currentThread().setContextClassLoader(savedClassLoader);
}
}
他的操作主要有两个,清除空闲连接方法evict
和保证连接池有足够空闲连接方法ensureMinIdle
清除空闲连接的方法主要做了两件事:回收过时的空闲连接,并验证未过时空闲连接,它的处理过程如下:
接下来看保证连接池有足够空闲连接方法,它主要负责当空闲连接小于最小空闲连接数时创建空闲连接:
在上数过程中,我们用到了哪些配置属性呢?
numTestsPerEvictionRun-每次检测空闲连接数
这个是用来设置我们的定时任务每次检测多少个空闲连接是需要销毁和验证的,默认值为3. 即每次任务只验证3个空闲连接是否销毁和有效。
maxIdle&minIdle
是否创建空闲连接的判断条件,默认值都为8,如果默认取最小,如果最小空闲连接>最大空闲连接,则以最大空闲连接为准。所以若想调整空闲连接数则需要把两个属性值同时调大。
testWhileIdle-验证空闲连接
为什么要验证空闲连接,因为如果我们长时间不连接mysql,mysql也会有空闲连接等待时长,如果不验证,则会因为超过了mysql的空闲连接等待时长导致我们连接池中的空闲连接不可用,所以需要验证空闲连接。
minEvictableIdleDuration&softMinEvictableIdleDuration
minEvictableIdleDuration
为空闲连接保留时长,默认为30分钟。如果超过了这个时间,空闲连接就不会保留了。
softMinEvictableIdleDuration
为空闲连接的软保留时长,默认为-1,即超过这个时间,但如果当前的空闲连接数还小于最小空闲连接数的话就还能继续保留。
lifo-空闲队列是否为后进先出队列
这个在Evictor管理创建空闲连接时,决定了我们创建的连接时放入队首还是队尾,很好理解的一点是,由于我们获取连接时我们都是从队首获取,如果设为后进先出队列,则意味着我们把空闲连接队列变为空闲连接栈了,有的空闲连接创建了之后可能一直没有使用就被回收掉,因此这种方式在我们设置空闲连接最大生存时间时,不建议采用。
对象连接池配置总结
属性 | 作用 | 默认值 |
---|---|---|
maxTotal |
连接池中允许的最大连接数量 | -1,不设限制 |
blockWhenExhausted |
连接耗尽时是否等待获取连接,如果阻塞为true则在连接池耗尽时,根据我们设置的maxWaitDuration 来尝试获取连接;如果为false则直接抛出异常 |
true |
maxWaitDuration |
获取连接的最大等待时间 | -1,不等待 |
lifo |
空闲连接队列是否为后进先出队列 | true |
testOnCreate |
连接创建后是否检测连接可用 | false |
testOnBorrow |
连接使用前是否检测连接可用 | false |
testOnReturn |
返回连接时是否检测连接可用 | false |
testWhileIdle |
定时管理任务是否验证空闲连接 | false |
durationBetweenEvictionRuns |
定时管理任务的执行时间间隔 | -1,不执行 |
numTestsPerEvictionRun |
定时管理任务每次检测的空闲连接数 | 3 |
minEvictableIdleDuration |
空闲连接保留时长 | 30X60X1000 |
softMinEvictableIdleDuration |
空闲连接的软保留时长 | -1 |
evictionPolicy |
空闲连接的销毁判断策略,可自定义 |
注意:验证sql和验证查询超时时间属于数据库连接属性,在创建数据库连接的工厂类中:
/**
* A {@link PooledObjectFactory} that creates {@link PoolableConnection}s.
*
* @since 2.0
*/
public class PoolableConnectionFactory implements PooledObjectFactory<PoolableConnection> {
...
private Collection<String> connectionInitSqls;
private Collection<String> disconnectionSqlCodes;
private Boolean defaultReadOnly;
private Boolean defaultAutoCommit;
private boolean autoCommitOnReturn = true;
private boolean rollbackOnReturn = true;
private int defaultTransactionIsolation = UNKNOWN_TRANSACTION_ISOLATION;
...
}
评论区