druid handleException执行流程源码解析
作者:codecraft
序
本文主要研究一下druid的handleException
prepareStatement
com/alibaba/druid/pool/DruidPooledConnection.java
public PreparedStatement prepareStatement(String sql) throws SQLException { checkState(); PreparedStatementHolder stmtHolder = null; PreparedStatementKey key = new PreparedStatementKey(sql, getCatalog(), MethodType.M1); boolean poolPreparedStatements = holder.isPoolPreparedStatements(); if (poolPreparedStatements) { stmtHolder = holder.getStatementPool().get(key); } if (stmtHolder == null) { try { stmtHolder = new PreparedStatementHolder(key, conn.prepareStatement(sql)); holder.getDataSource().incrementPreparedStatementCount(); } catch (SQLException ex) { handleException(ex, sql); } } initStatement(stmtHolder); DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder); holder.addTrace(rtnVal); return rtnVal; }
DruidPooledConnection的prepareStatement会catch住SQLException然后执行handleException
executeQuery
com/alibaba/druid/pool/DruidPooledPreparedStatement.java
public ResultSet executeQuery() throws SQLException { checkOpen(); incrementExecuteQueryCount(); transactionRecord(sql); oracleSetRowPrefetch(); conn.beforeExecute(); try { ResultSet rs = stmt.executeQuery(); if (rs == null) { return null; } DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs); addResultSetTrace(poolableResultSet); return poolableResultSet; } catch (Throwable t) { errorCheck(t); throw checkException(t); } finally { conn.afterExecute(); } }
executeQuery在catch到Throwable时会执行throw checkException(t)
checkException
com/alibaba/druid/pool/DruidPooledStatement.java
protected SQLException checkException(Throwable error) throws SQLException { String sql = null; if (this instanceof DruidPooledPreparedStatement) { sql = ((DruidPooledPreparedStatement) this).getSql(); } handleSocketTimeout(error); exceptionCount++; return conn.handleException(error, sql); }
checkException这里会执行conn.handleException(error, sql)
handleException
public SQLException handleException(Throwable t, String sql) throws SQLException { final DruidConnectionHolder holder = this.holder; // if (holder != null) { DruidAbstractDataSource dataSource = holder.getDataSource(); dataSource.handleConnectionException(this, t, sql); } if (t instanceof SQLException) { throw (SQLException) t; } throw new SQLException("Error", t); }
handleException这里是委托给了dataSource.handleConnectionException(this, t, sql);
handleConnectionException
com/alibaba/druid/pool/DruidDataSource.java
public void handleConnectionException(DruidPooledConnection pooledConnection, Throwable t, String sql) throws SQLException { final DruidConnectionHolder holder = pooledConnection.getConnectionHolder(); if (holder == null) { return; } errorCountUpdater.incrementAndGet(this); lastError = t; lastErrorTimeMillis = System.currentTimeMillis(); if (t instanceof SQLException) { SQLException sqlEx = (SQLException) t; // broadcastConnectionError ConnectionEvent event = new ConnectionEvent(pooledConnection, sqlEx); for (ConnectionEventListener eventListener : holder.getConnectionEventListeners()) { eventListener.connectionErrorOccurred(event); } // exceptionSorter.isExceptionFatal if (exceptionSorter != null && exceptionSorter.isExceptionFatal(sqlEx)) { handleFatalError(pooledConnection, sqlEx, sql); } throw sqlEx; } else { throw new SQLException("Error", t); } }
handleConnectionException方法在exceptionSorter.isExceptionFatal(sqlEx)为true时会执行handleFatalError
isExceptionFatal
com/alibaba/druid/pool/vendor/MySqlExceptionSorter.java
public class MySqlExceptionSorter implements ExceptionSorter { @Override public boolean isExceptionFatal(SQLException e) { if (e instanceof SQLRecoverableException) { return true; } final String sqlState = e.getSQLState(); final int errorCode = e.getErrorCode(); if (sqlState != null && sqlState.startsWith("08")) { return true; } switch (errorCode) { // Communications Errors case 1040: // ER_CON_COUNT_ERROR case 1042: // ER_BAD_HOST_ERROR case 1043: // ER_HANDSHAKE_ERROR case 1047: // ER_UNKNOWN_COM_ERROR case 1081: // ER_IPSOCK_ERROR case 1129: // ER_HOST_IS_BLOCKED case 1130: // ER_HOST_NOT_PRIVILEGED // Authentication Errors case 1045: // ER_ACCESS_DENIED_ERROR // Resource errors case 1004: // ER_CANT_CREATE_FILE case 1005: // ER_CANT_CREATE_TABLE case 1015: // ER_CANT_LOCK case 1021: // ER_DISK_FULL case 1041: // ER_OUT_OF_RESOURCES // Out-of-memory errors case 1037: // ER_OUTOFMEMORY case 1038: // ER_OUT_OF_SORTMEMORY // Access denied case 1142: // ER_TABLEACCESS_DENIED_ERROR case 1227: // ER_SPECIFIC_ACCESS_DENIED_ERROR case 1023: // ER_ERROR_ON_CLOSE case 1290: // ER_OPTION_PREVENTS_STATEMENT return true; default: break; } // for oceanbase if (errorCode >= -9000 && errorCode <= -8000) { return true; } String className = e.getClass().getName(); if (className.endsWith(".CommunicationsException")) { return true; } String message = e.getMessage(); if (message != null && message.length() > 0) { if (message.startsWith("Streaming result set com.mysql.jdbc.RowDataDynamic") && message.endsWith("is still active. No statements may be issued when any streaming result sets are open and in use on a given connection. Ensure that you have called .close() on any active streaming result sets before attempting more queries.")) { return true; } final String errorText = message.toUpperCase(); if ((errorCode == 0 && (errorText.contains("COMMUNICATIONS LINK FAILURE")) // || errorText.contains("COULD NOT CREATE CONNECTION")) // || errorText.contains("NO DATASOURCE") // || errorText.contains("NO ALIVE DATASOURCE")) { return true; } } Throwable cause = e.getCause(); for (int i = 0; i < 5 && cause != null; ++i) { if (cause instanceof SocketTimeoutException) { return true; } className = cause.getClass().getName(); if (className.endsWith(".CommunicationsException")) { return true; } cause = cause.getCause(); } return false; } @Override public void configFromProperties(Properties properties) { } }
MySqlExceptionSorter针对指定错误码来判断是否fatal
handleFatalError
com/alibaba/druid/pool/DruidDataSource.java
protected final void handleFatalError(DruidPooledConnection conn, SQLException error, String sql) throws SQLException { final DruidConnectionHolder holder = conn.holder; if (conn.isTraceEnable()) { activeConnectionLock.lock(); try { if (conn.isTraceEnable()) { activeConnections.remove(conn); conn.setTraceEnable(false); } } finally { activeConnectionLock.unlock(); } } long lastErrorTimeMillis = this.lastErrorTimeMillis; if (lastErrorTimeMillis == 0) { lastErrorTimeMillis = System.currentTimeMillis(); } if (sql != null && sql.length() > 1024) { sql = sql.substring(0, 1024); } boolean requireDiscard = false; final ReentrantLock lock = conn.lock; lock.lock(); try { if ((!conn.isClosed()) || !conn.isDisable()) { conn.disable(error); requireDiscard = true; } lastFatalErrorTimeMillis = lastErrorTimeMillis; fatalErrorCount++; if (fatalErrorCount - fatalErrorCountLastShrink > onFatalErrorMaxActive) { onFatalError = true; } lastFatalError = error; lastFatalErrorSql = sql; } finally { lock.unlock(); } if (onFatalError && holder != null && holder.getDataSource() != null) { ReentrantLock dataSourceLock = holder.getDataSource().lock; dataSourceLock.lock(); try { emptySignal(); } finally { dataSourceLock.unlock(); } } if (requireDiscard) { if (holder.statementTrace != null) { holder.lock.lock(); try { for (Statement stmt : holder.statementTrace) { JdbcUtils.close(stmt); } } finally { holder.lock.unlock(); } } this.discardConnection(holder); } // holder. LOG.error("{conn-" + holder.getConnectionId() + "} discard", error); }
handleFatalError方法这里会执行conn.disable(error),然后标记requireDiscard为true,最后执行discardConnection
discardConnection
com/alibaba/druid/pool/DruidDataSource.java
public void discardConnection(DruidConnectionHolder holder) { if (holder == null) { return; } Connection conn = holder.getConnection(); if (conn != null) { JdbcUtils.close(conn); } lock.lock(); try { if (holder.discard) { return; } if (holder.active) { activeCount--; holder.active = false; } discardCount++; holder.discard = true; if (activeCount <= minIdle) { emptySignal(); } } finally { lock.unlock(); } }
discardConnection这里会通过JdbcUtils.close(conn)关闭连接,然后加锁判断是否小于等于minIdle,若为true则执行emptySignal
小结
druid会在prepareStatement或者执行prepareStatement出现异常的时候执行conn.handleException,它委托给dataSource.handleConnectionException,后者会在exceptionSorter.isExceptionFatal(sqlEx)为true时会执行handleFatalError,handleFatalError方法这里会执行conn.disable(error),然后标记requireDiscard为true,最后执行discardConnection来关闭连接。通过这样子来快速清理不可用的连接,避免连接池的连接不可用。
以上就是druid handleException执行流程源码解析的详细内容,更多关于druid handleException执行的资料请关注脚本之家其它相关文章!