上段时间,遇到一个这样的问题,由于数据显示的原因,需要往Spring Boot项目中配置两个数据库连接池,一个是mysql,另外一个是amazon redshift。
情景
接着前言,当连上两个数据库后,发生一个问题,由于redshift主要定位是数据仓库,所以对于频繁的查询插入操作会比较慢,另外,由于redshift数据库连接数配置问题,会导致Druid连接被远程关闭。经常性会报500错误,实际是time_out。
其实这样原因也挺正常,remote拒绝连接,所以把我的连接关闭了。
Druid日志循环输出
由于数据库连接的关闭,Druid就循环尝试连接,当连接失败,就会循环记录error:
LOG.error("create connection OutOfMemoryError, out memory. ", e);
首先一个尝试连接的线程:
@Override
public void run() {
runInternal();
}
看runlnternal方法:
private void runInternal() {
for (;;) {
// addLast
lock.lock();
try {
if (closed || closing) {
createTaskCount--;
return;
}
boolean emptyWait = true;
if (createError != null && poolingCount == 0) {
emptyWait = false;
}
if (emptyWait) {
// 必须存在线程等待,才创建连接
if (poolingCount >= notEmptyWaitThreadCount //
&& !(keepAlive && activeCount + poolingCount < minIdle)
&& !initTask) {
createTaskCount--;
return;
}
// 防止创建超过maxActive数量的连接
if (activeCount + poolingCount >= maxActive) {
createTaskCount--;
return;
}
}
} finally {
lock.unlock();
}
PhysicalConnectionInfo physicalConnection = null;
try {
physicalConnection = createPhysicalConnection();
setFailContinuous(false);
} catch (OutOfMemoryError e) {
LOG.error("create connection OutOfMemoryError, out memory. ", e);
errorCount++;
if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {
// fail over retry attempts
setFailContinuous(true);
if (failFast) {
lock.lock();
try {
notEmpty.signalAll();
} finally {
lock.unlock();
}
}
if (breakAfterAcquireFailure) {
lock.lock();
try {
createTaskCount--;
} finally {
lock.unlock();
}
return;
}
this.errorCount = 0; // reset errorCount
if (closing || closed) {
createTaskCount--;
return;
}
createSchedulerFuture = createScheduler.schedule(this, timeBetweenConnectErrorMillis, TimeUnit.MILLISECONDS);
return;
}
} catch (SQLException e) {
LOG.error("create connection SQLException, url: " + jdbcUrl, e);
errorCount++;
if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {
// fail over retry attempts
setFailContinuous(true);
if (failFast) {
lock.lock();
try {
notEmpty.signalAll();
} finally {
lock.unlock();
}
}
if (breakAfterAcquireFailure) {
lock.lock();
try {
createTaskCount--;
} finally {
lock.unlock();
}
return;
}
this.errorCount = 0; // reset errorCount
if (closing || closed) {
createTaskCount--;
return;
}
createSchedulerFuture = createScheduler.schedule(this, timeBetweenConnectErrorMillis, TimeUnit.MILLISECONDS);
return;
}
} catch (RuntimeException e) {
LOG.error("create connection RuntimeException", e);
// unknow fatal exception
setFailContinuous(true);
continue;
} catch (Error e) {
lock.lock();
try {
createTaskCount--;
} finally {
lock.unlock();
}
LOG.error("create connection Error", e);
// unknow fatal exception
setFailContinuous(true);
break;
} catch (Throwable e) {
LOG.error("create connection unexecpted error.", e);
break;
}
if (physicalConnection == null) {
continue;
}
boolean result = put(physicalConnection);
if (!result) {
JdbcUtils.close(physicalConnection.getPhysicalConnection());
LOG.info("put physical connection to pool failed.");
}
break;
}
}
在runInternal
中,可以看出,一个for的死循环,无限次尝试连接,所以失败就记录错误。
最终在console或者log文件里,将会有一大片日志。
我开始尝试寻找一个参数,能够设置尝试次数,如果超过多少次,就放弃重连,但是没有找到。。
在代码中,不过有一个这样参数:
breakAfterAcquireFailure
由名字可以知道,就是当连接失败,就不进行尝试重连,也就是这样完全放弃重试了。
该值默认是false,也就是默认尝试连接
尾声
当然最终的breakAfterAcquireFailure也无法满足我的要求,当然最终是换了另外一种思路解决这个问题,请看下一篇文章。