druid 使用过程中遇到的坑
最近在写一个程序有80个数据库用户,如果全部走配置,那么不是我要疯掉,就是部署人员要疯掉。因此考虑从将数据库信息存放在数据库参数表,然后在程序启动后再去初始化80个数据库连接池。initialSize设置为0。好,现在启动正常,程序正常运行。但是当有业务过来,需要操作数据库时发现日志程序报java.sql.SQLException: Unknown host specified如下 :
查证后发现数据库Url配置错误。但是发现日志里边报java.sql.SQLException: Unknown host specified异常,最后线程报线程池满了异常如下:
看到这样实在想不通,但是又不能只改数据库URL地址,让正常允许,那如果生产环境有人不小心配错,程序正常启动,但是操作数据库时报如下错误,那我岂不是要丢了饭碗,于是只能静下心来扒代码了。找到错误行,debug运行发现DruidDataSource.CreateConnectionThread .run方法如下:
public void run() {
initedLatch.countDown();
int errorCount = 0;
for (;;) {
// addLast
try {
lock.lockInterruptibly();
} catch (InterruptedException e2) {
break;
}
try {
// 必须存在线程等待,才创建连接
if (poolingCount >= notEmptyWaitThreadCount) {
empty.await();
}
// 防止创建超过maxActive数量的连接
if (activeCount + poolingCount >= maxActive) {
empty.await();
continue;
}
} catch (InterruptedException e) {
lastCreateError = e;
lastErrorTimeMillis = System.currentTimeMillis();
break;
} finally {
lock.unlock();
}
Connection connection = null;
try {
connection = createPhysicalConnection();
} catch (SQLException e) {
LOG.error("create connection error", e);
errorCount++;
if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {
if (breakAfterAcquireFailure) {
break;
}
try {
Thread.sleep(timeBetweenConnectErrorMillis);
} catch (InterruptedException interruptEx) {
break;
}
}
} catch (RuntimeException e) {
LOG.error("create connection error", e);
continue;
} catch (Error e) {
LOG.error("create connection error", e);
break;
}
if (connection == null) {
continue;
}
DruidConnectionHolder holder = null;
try {
holder = new DruidConnectionHolder(DruidDataSource.this, connection);
} catch (SQLException ex) {
LOG.error("create connection holder error", ex);
break;
}
lock.lock();
try {
connections[poolingCount++] = holder;
if (poolingCount > poolingPeak) {
poolingPeak = poolingCount;
poolingPeakTime = System.currentTimeMillis();
}
errorCount = 0; // reset errorCount
notEmpty.signal();
notEmptySignalCount++;
} finally {
lock.unlock();
}
}
}
}
run方法里边是一个死循环创建Connection连接,根据条件跳出。看了代码发现只有errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0 并且breakAfterAcquireFailure=true才有可能会进入break。于是一顿操作猛如虎修改配置,再次测试,发现连接错误后程序for循环是跳出了,但是程序卡死了,没有办法跳出这个方法了。又去扒代码,发现这个直接跳出循环,程序没有unlock掉。
被逼无奈有懒得去改底层,改完要测试好几个模块,太麻烦了。只能将这个方法配置在xml里边,在程序启动的时候去初始化线程池,但是要注意initialSize不要设置成0.要不然启动的时候不回去测试是否可以连接,如果你后边用的时候才去测试一旦数据库配置错误会发生相同的问题。
-------------------第一次写,有些不足的地方,请大家见谅