Druid 1.1.24 在控制台打印"discard long time none received connection. , jdbcUrl : jdbc:mysql://..."錯誤日志
參考資料
報錯原因
- 我使用的是mysql 數據庫,驅動包使用的是mysql-connector-java-8.0.27.jar
- druid 使用的是 druid-1.1.24.jar
- 經過每個包的翻看,在 com.alibaba.druid.pool.vendor 包下查到這個MySqlValidConnectionChecker類 ,根據類名猜測出這個是 MySQL 數據連接校驗類.
- MySqlValidConnectionChecker 源碼如下:
點擊查看代碼
public class MySqlValidConnectionChecker extends ValidConnectionCheckerAdapter implements ValidConnectionChecker, Serializable {
public static final int DEFAULT_VALIDATION_QUERY_TIMEOUT = 1;
public static final String DEFAULT_VALIDATION_QUERY = "SELECT 1";
private static final long serialVersionUID = 1L;
private static final Log LOG = LogFactory.getLog(MySqlValidConnectionChecker.class);
private Class<?> clazz;
private Method ping;
private boolean usePingMethod = false;
public MySqlValidConnectionChecker(){
try {
clazz = Utils.loadClass("com.mysql.jdbc.MySQLConnection");
if (clazz == null) {
clazz = Utils.loadClass("com.mysql.cj.jdbc.ConnectionImpl");
}
if (clazz != null) {
ping = clazz.getMethod("pingInternal", boolean.class, int.class);
}
if (ping != null) {
usePingMethod = true;
}
} catch (Exception e) {
LOG.warn("Cannot resolve com.mysql.jdbc.Connection.ping method. Will use 'SELECT 1' instead.", e);
}
configFromProperties(System.getProperties());
}
@Override
public void configFromProperties(Properties properties) {
String property = properties.getProperty("druid.mysql.usePingMethod");
if ("true".equals(property)) {
setUsePingMethod(true);
} else if ("false".equals(property)) {
setUsePingMethod(false);
}
}
public boolean isUsePingMethod() {
return usePingMethod;
}
public void setUsePingMethod(boolean usePingMethod) {
this.usePingMethod = usePingMethod;
}
public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
if (conn.isClosed()) {
return false;
}
if (usePingMethod) {
if (conn instanceof DruidPooledConnection) {
conn = ((DruidPooledConnection) conn).getConnection();
}
if (conn instanceof ConnectionProxy) {
conn = ((ConnectionProxy) conn).getRawObject();
}
if (clazz.isAssignableFrom(conn.getClass())) {
if (validationQueryTimeout <= 0) {
validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;
}
try {
ping.invoke(conn, true, validationQueryTimeout * 1000);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof SQLException) {
throw (SQLException) cause;
}
throw e;
}
return true;
}
}
String query = validateQuery;
if (validateQuery == null || validateQuery.isEmpty()) {
query = DEFAULT_VALIDATION_QUERY;
}
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
if (validationQueryTimeout > 0) {
stmt.setQueryTimeout(validationQueryTimeout);
}
rs = stmt.executeQuery(query);
return true;
} finally {
JdbcUtils.close(rs);
JdbcUtils.close(stmt);
}
}
}
- 根據源碼中 isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) 方法,有個判斷 :
// 如果使用 ping 方法,則會反射調用 ping 方法驗證數據庫連接是否有效,而不執行 validateQuery 驗證.
if (usePingMethod) {
if (conn instanceof DruidPooledConnection) {
conn = ((DruidPooledConnection) conn).getConnection();
}
if (conn instanceof ConnectionProxy) {
conn = ((ConnectionProxy) conn).getRawObject();
}
if (clazz.isAssignableFrom(conn.getClass())) {
if (validationQueryTimeout <= 0) {
validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;
}
try {
ping.invoke(conn, true, validationQueryTimeout * 1000);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof SQLException) {
throw (SQLException) cause;
}
throw e;
}
return true;
}
}
那么 usePingMethod 是怎么來的?
查看構造器方法:
public MySqlValidConnectionChecker(){
try {
// 先加載的驅動類
clazz = Utils.loadClass("com.mysql.jdbc.MySQLConnection");
if (clazz == null) {
clazz = Utils.loadClass("com.mysql.cj.jdbc.ConnectionImpl");
}
// 反射獲取驅動類的 pingInternal 方法
if (clazz != null) {
ping = clazz.getMethod("pingInternal", boolean.class, int.class);
}
// 若驅動類的 pingInternal 方法如果存在則 usePingMethod = true
if (ping != null) {
usePingMethod = true;
}
} catch (Exception e) {
LOG.warn("Cannot resolve com.mysql.jdbc.Connection.ping method. Will use 'SELECT 1' instead.", e);
}
// 從系統配置文件配置屬性,
configFromProperties(System.getProperties());
}
@Override
public void configFromProperties(Properties properties) {
// 從系統屬性(JVM啟動參數)中獲取'druid.mysql.usePingMethod'屬性 覆蓋 usePingMethod 的值
String property = properties.getProperty("druid.mysql.usePingMethod");
if ("true".equals(property)) {
setUsePingMethod(true);
} else if ("false".equals(property)) {
setUsePingMethod(false);
}
}
以上可以得出結論, 通過配置JVM啟動參數"druid.mysql.usePingMethod=false" 告知 Druid 停止使用 usePingMethod 方法驗證數據庫連接,轉而使用 validateQuery 驗證.
解決方法
1. JVM啟動參數添加如下配置
-Ddruid.mysql.usePingMethod=false