mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
Optimize the Validation logic of RandomDataSource
treating normal SQL execution in the pool as validation, so we can skip some validation operations
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import com.alibaba.druid.filter.FilterEventAdapter;
|
||||
import com.alibaba.druid.proxy.jdbc.ConnectionProxy;
|
||||
import com.alibaba.druid.proxy.jdbc.DataSourceProxy;
|
||||
import com.alibaba.druid.proxy.jdbc.ResultSetProxy;
|
||||
import com.alibaba.druid.proxy.jdbc.StatementProxy;
|
||||
|
||||
/**
|
||||
* A Druid Filter that records the last success execute time.
|
||||
*
|
||||
* @author DigitalSonic
|
||||
*/
|
||||
public class RandomDataSourceValidateFilter extends FilterEventAdapter {
|
||||
protected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) {
|
||||
recordTime(statement);
|
||||
}
|
||||
|
||||
protected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) {
|
||||
recordTime(statement);
|
||||
}
|
||||
|
||||
protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) {
|
||||
recordTime(statement);
|
||||
}
|
||||
|
||||
protected void statementExecuteBatchAfter(StatementProxy statement, int[] result) {
|
||||
recordTime(statement);
|
||||
}
|
||||
|
||||
private void recordTime(StatementProxy statement) {
|
||||
ConnectionProxy conn = statement.getConnectionProxy();
|
||||
if (conn != null) {
|
||||
DataSourceProxy dataSource = conn.getDirectDataSource();
|
||||
RandomDataSourceValidateThread.logSuccessTime(dataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
-12
@@ -17,7 +17,6 @@ package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.Driver;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -30,9 +29,11 @@ import java.util.concurrent.Executors;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.proxy.jdbc.DataSourceProxy;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
import com.alibaba.druid.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A Thread trying to test all DataSource provided by HADataSource.
|
||||
@@ -42,12 +43,24 @@ import com.alibaba.druid.util.JdbcUtils;
|
||||
*/
|
||||
public class RandomDataSourceValidateThread implements Runnable {
|
||||
private final static Log LOG = LogFactory.getLog(RandomDataSourceValidateThread.class);
|
||||
private int checkingIntervalSeconds = 15;
|
||||
private final static Map<String, Long> SUCCESS_TIMES = new ConcurrentHashMap<String, Long>();
|
||||
|
||||
private int checkingIntervalSeconds = 10; // This value should NOT be too small.
|
||||
private int validationSleepSeconds = 0;
|
||||
private int blacklistThreshold = 3;
|
||||
private RandomDataSourceSelector selector;
|
||||
private ExecutorService checkExecutor = Executors.newFixedThreadPool(5);
|
||||
private Map<String, Integer> errorCounts = new ConcurrentHashMap<String, Integer>();
|
||||
private Map<String, Long> lastCheckTimes = new ConcurrentHashMap<String, Long>();
|
||||
|
||||
/**
|
||||
* Provide a static method to record the last success time of a DataSource
|
||||
*/
|
||||
public static void logSuccessTime(DataSourceProxy dataSource) {
|
||||
if (dataSource != null && !StringUtils.isEmpty(dataSource.getName())) {
|
||||
SUCCESS_TIMES.put(dataSource.getName(), System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
public RandomDataSourceValidateThread(RandomDataSourceSelector selector) {
|
||||
this.selector = selector;
|
||||
@@ -88,12 +101,13 @@ public class RandomDataSourceValidateThread implements Runnable {
|
||||
private void maintainBlacklist() {
|
||||
Map<String, DataSource> dataSourceMap = selector.getDataSourceMap();
|
||||
for (Map.Entry<String, Integer> e : errorCounts.entrySet()) {
|
||||
DataSource dataSource = dataSourceMap.get(e.getKey());
|
||||
if (e.getValue() <= 0) {
|
||||
selector.removeBlacklist(dataSourceMap.get(e.getKey()));
|
||||
selector.removeBlacklist(dataSource);
|
||||
} else if (e.getValue() >= blacklistThreshold
|
||||
&& !selector.containInBlacklist(dataSourceMap.get(e.getKey()))) {
|
||||
&& !selector.containInBlacklist(dataSource)) {
|
||||
LOG.warn("Adding " + e.getKey() + " to blacklist.");
|
||||
selector.addBlacklist(dataSourceMap.get(e.getKey()));
|
||||
selector.addBlacklist(dataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,17 +129,26 @@ public class RandomDataSourceValidateThread implements Runnable {
|
||||
tasks.add(new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() {
|
||||
boolean flag = check(e.getKey(), (DruidDataSource) e.getValue());
|
||||
String key = e.getKey();
|
||||
DruidDataSource dataSource = (DruidDataSource) e.getValue();
|
||||
|
||||
if (isSkipChecking(key, dataSource)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean flag = check(key, dataSource);
|
||||
|
||||
if (flag) {
|
||||
errorCounts.put(e.getKey(), 0);
|
||||
logSuccessTime(dataSource);
|
||||
errorCounts.put(key, 0);
|
||||
} else {
|
||||
if (!errorCounts.containsKey(e.getKey())) {
|
||||
errorCounts.put(e.getKey(), 0);
|
||||
if (!errorCounts.containsKey(key)) {
|
||||
errorCounts.put(key, 0);
|
||||
}
|
||||
int count = errorCounts.get(e.getKey());
|
||||
errorCounts.put(e.getKey(), count + 1);
|
||||
int count = errorCounts.get(key);
|
||||
errorCounts.put(key, count + 1);
|
||||
}
|
||||
lastCheckTimes.put(dataSource.getName(), System.currentTimeMillis());
|
||||
|
||||
return flag;
|
||||
}
|
||||
@@ -138,6 +161,16 @@ public class RandomDataSourceValidateThread implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSkipChecking(String key, DruidDataSource dataSource) {
|
||||
Long lastSuccessTime = SUCCESS_TIMES.get(dataSource.getName());
|
||||
Long lastCheckTime = lastCheckTimes.get(dataSource.getName());
|
||||
|
||||
return lastSuccessTime != null && lastCheckTime != null
|
||||
&& (System.currentTimeMillis() - lastSuccessTime) <= checkingIntervalSeconds * 1000
|
||||
&& (System.currentTimeMillis() - lastCheckTime) <= 5 * checkingIntervalSeconds * 1000
|
||||
&& (!errorCounts.containsKey(key) || errorCounts.get(key) < 1);
|
||||
}
|
||||
|
||||
private boolean check(String name, DruidDataSource dataSource) {
|
||||
boolean result = true;
|
||||
Driver driver = dataSource.getRawDriver();
|
||||
@@ -158,7 +191,7 @@ public class RandomDataSourceValidateThread implements Runnable {
|
||||
conn = driver.connect(url, info);
|
||||
sleepBeforeValidation();
|
||||
dataSource.validateConnection(conn);
|
||||
} catch (SQLException e) {
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Validation FAILED for " + name + " with url [" + url + "] and username ["
|
||||
+ info.getProperty("user") + "]. Exception: " + e.getMessage());
|
||||
result = false;
|
||||
|
||||
@@ -9,4 +9,5 @@ druid.filters.slf4j=com.alibaba.druid.filter.logging.Slf4jLogFilter
|
||||
druid.filters.commonlogging=com.alibaba.druid.filter.logging.CommonsLogFilter
|
||||
druid.filters.commonLogging=com.alibaba.druid.filter.logging.CommonsLogFilter
|
||||
druid.filters.wall=com.alibaba.druid.wall.WallFilter
|
||||
druid.filters.config=com.alibaba.druid.filter.config.ConfigFilter
|
||||
druid.filters.config=com.alibaba.druid.filter.config.ConfigFilter
|
||||
druid.filters.haRandomValidator=com.alibaba.druid.pool.ha.selector.RandomDataSourceValidateFilter
|
||||
Reference in New Issue
Block a user