看一段druid創建連接的源碼:
public class CreateConnectionThread extends Thread {
public CreateConnectionThread(String name){
super(name);
this.setDaemon(true);
}
public void run() {
initedLatch.countDown();
long lastDiscardCount = 0;
int errorCount = 0;
for (;;) {
try {
lock.lockInterruptibly();
} catch (InterruptedException e2) {
break;
}
long discardCount = DruidDataSource.this.discardCount;
boolean discardChanged = discardCount - lastDiscardCount > 0;
lastDiscardCount = discardCount;
try {
boolean emptyWait = true;
if (createError != null
&& poolingCount == 0
&& !discardChanged) {
emptyWait = false;
}
if (emptyWait
&& asyncInit && createCount.get() < initialSize) {
emptyWait = false;
}
if (emptyWait) {
if (poolingCount >= notEmptyWaitThreadCount //
&& !(keepAlive && activeCount + poolingCount < minIdle)) {
empty.await();
}
if (activeCount + poolingCount >= maxActive) {
empty.await();
continue;
}
}
} catch (InterruptedException e) {
lastCreateError = e;
lastErrorTimeMillis = System.currentTimeMillis();
if (!closing) {
LOG.error("create connection Thread Interrupted, url: " + jdbcUrl, e);
}
break;
} finally {
lock.unlock();
}
PhysicalConnectionInfo connection = null;
try {
connection = createPhysicalConnection();
setFailContinuous(false);
} catch (SQLException e) {
LOG.error("create connection SQLException, url: " + jdbcUrl + ", errorCode " + e.getErrorCode()
+ ", state " + e.getSQLState(), e);
errorCount++;
if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {
setFailContinuous(true);
if (failFast) {
lock.lock();
try {
notEmpty.signalAll();
} finally {
lock.unlock();
}
}
//(1)如果獲取失敗跳出
if (breakAfterAcquireFailure) {
break;
}
try {
Thread.sleep(timeBetweenConnectErrorMillis);
} catch (InterruptedException interruptEx) {
break;
}
}
} catch (RuntimeException e) {
LOG.error("create connection RuntimeException", e);
setFailContinuous(true);
continue;
} catch (Error e) {
LOG.error("create connection Error", e);
setFailContinuous(true);
break;
}
// (2)未獲取到連接繼續
if (connection == null) {
continue;
}
boolean result = put(connection);
if (!result) {
JdbcUtils.close(connection.getPhysicalConnection());
LOG.info("put physical connection to pool failed.");
}
errorCount = 0; // reset errorCount
}
}
}
以上兩個注釋處,有兩個關鍵點:
- 注釋(2)處,如果獲取失敗(connection == null),則繼續獲取。這里是不停嘗試重連的原因所在。
- 注釋(1)處,如果獲取失敗,則放棄獲取直接跳出。
所以容易看出,只要給(1)處的 breakAfterAcquireFailure 置為true,就可以解決問題, 然而,druid的配置項中並沒有這個選項,但我們發現,DruidDataSource提供了setBreakAfterAcquireFailure(boolean)方法。
再看一段通常我們創建數據源會寫的代碼:
Properties p =new Properties();
p.put("url", url);
p.put("username", username);
p.put("password", password);
DataSource dataSource = DruidDataSourceFactory.createDataSource(p);
DruidDataSourceFactory.createDataSource() 方法內部實現:
.....// 這里省略了很多的讀取一些properteis配置的代碼
value = (String) properties.get(“init”);
if ("true".equals(value)) {
dataSource.init();
}
注意看,這里,通過讀取init配置項來判斷是否要初始化數據源。而我們現在要做的是:將breakAfterAcquireFailure 置為true,而配置項中不存在,所以我們需要將init設置為false,然后手動setBreakAfterAcquireFailure(true),然后手動init數據源。所以有以下方案:
public DataSource build(String url, String username, String password) throws Exception {
Properties p =new Properties();
p.put("init","false");
p.put("url", url);
p.put("username", username);
p.put("password", password);
DruidDataSource dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(p);
dataSource.setBreakAfterAcquireFailure(true);
dataSource.init();
return dataSource;
}
通過構建完的DataSource替換系統中的datasource生成即可。