在对数据库做健康探测的时候,出现无法在获取连接时得到当前连接状态的情况。
通过DataSource对象的getConnection方法获取connection
在获取到连接之后有三个配置会使得当前连接被验证是否正常
- testOnBorrow:只要当前值为true,获取到的连接就会被验证,并且如果连接不可用马上就会被回收,然后获取下个连接
- testWhileIdle:该配置需要当前系统时间,当前连接的lastActiveTimeMillis(lastKeepTimeMillis,当lastkeepTimeMills大于lastActiveTimeMills时),以及timeBetweenEvictionRunsMillis这三个时间来决定是否进行连接验证
- removeAbandoned:如果当前值设置为true,则在获取连接的时候会将当前的连接放到activeconnections集合中。然后注册一个定时任务循环遍历当前集合中的connection,发现无用的就移除。
if (removeAbandoned) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
poolableConnection.connectStackTrace = stackTrace;
poolableConnection.setConnectedTimeNano();
poolableConnection.traceEnable = true;
activeConnectionLock.lock();
try {
activeConnections.put(poolableConnection, PRESENT);
} finally {
activeConnectionLock.unlock();
}
}
protected void createAndStartDestroyThread() {
destroyTask = new DestroyTask();
if (destroyScheduler != null) {
long period = timeBetweenEvictionRunsMillis;
if (period <= 0) {
period = 1000;
}
destroySchedulerFuture = destroyScheduler.scheduleAtFixedRate(destroyTask, period, period,
TimeUnit.MILLISECONDS);
initedLatch.countDown();
return;
}
String threadName = "Druid-ConnectionPool-Destroy-" + System.identityHashCode(this);
destroyConnectionThread = new DestroyConnectionThread(threadName);
destroyConnectionThread.start();
}
public class DestroyTask implements Runnable {
public DestroyTask() {
}
@Override
public void run() {
shrink(true, keepAlive);
if (isRemoveAbandoned()) {
removeAbandoned();
}
}
}
activeConnectionLock.lock();
try {
Iterator<DruidPooledConnection> iter = activeConnections.keySet().iterator();
for (; iter.hasNext();) {
DruidPooledConnection pooledConnection = iter.next();
if (pooledConnection.isRunning()) {
continue;
}
long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);
if (timeMillis >= removeAbandonedTimeoutMillis) {
iter.remove();
pooledConnection.setTraceEnable(false);
abandonedList.add(pooledConnection);
}
}
} finally {
activeConnectionLock.unlock();
}
数据库操作时获取到连接之后执行SQL的情况下的超时管理
- socket timeout 管理, 这个属性可以在数据库设置(在jdbc中是否可以设置?)
- statement timeout 管理,这里需要注意一点这里的超时管理是注册的定时任务
- transaction timeout 管理,Spring的@transaction中有timeout属性
三个超时直接transaction timeout依赖statement timeout,statement timeout依赖socket timeout
PrepareStatement preparement = connection.prepareStatement("select 1");
// statement 层面的超时管理
preparement.setQueryTimeout(1);
boolean result = preparement.execute();
// Exception in thread "main" com.mysql.cj.jdbc.exceptions.MySQLTimeoutException: Statement cancelled due to timeout or client request
参考:
https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8
https://github.com/alibaba/druid/wiki/ExceptionSorter_cn
https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
https://blog.csdn.net/weixin_28707365/article/details/113241314