When using a personal Alibaba cloud tester, when querying the real-time output log, you see that the database connection fails, and the server has been reconnecting to the server. At first, I thought it was a repeated attack. Later, after restarting the service, there was no continuous reconnection. See the following output log:
2022-02-09 11:04:58.896 ERROR 16876 --- [eate-1550991149] com.alibaba.druid.pool.DruidDataSource : create connection SQLException, url: jdbc:mysql://47.98.67,98:1234/test?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC, errorCode 1045, state 28000 java.sql.SQLException: Access denied for user 'root'@'113.90.123.76' (using password: YES) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.16.jar:8.0.16] at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.16.jar:8.0.16] at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.16.jar:8.0.16] at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:835) ~[mysql-connector-java-8.0.16.jar:8.0.16] at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:455) ~[mysql-connector-java-8.0.16.jar:8.0.16] at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:240) ~[mysql-connector-java-8.0.16.jar:8.0.16] at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:199) ~[mysql-connector-java-8.0.16.jar:8.0.16] at com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl.java:156) ~[druid-1.1.10.jar:1.1.10] at com.alibaba.druid.filter.stat.StatFilter.connection_connect(StatFilter.java:218) ~[druid-1.1.10.jar:1.1.10] at com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl.java:150) ~[druid-1.1.10.jar:1.1.10] at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1560) ~[druid-1.1.10.jar:1.1.10] at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1623) ~[druid-1.1.10.jar:1.1.10] at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2468) ~[druid-1.1.10.jar:1.1.10]
Note that the prompt of druid database connection pool has always been mentioned above. Here, it is thought that it may be the problem of druid connection pool. Then, after removing the druid Maven dependency, there will be no reconnection problem on the request interface.
Reason for druid reconnection
Find the last line of druiddatasource in the source code above Java: 2468 locate the source code and create a connection thread with CreateConnectionThread. Take a look at the source code of CreateConnectionThread:
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 (;;) { // addLast 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 < initialSize) { emptyWait = false; } if (emptyWait) { // A thread wait must exist before a connection can be created if (poolingCount >= notEmptyWaitThreadCount // && !(keepAlive && activeCount + poolingCount < minIdle)) { empty.await(); } // Prevent creating more connections than maxActive 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) { // fail over retry attempts setFailContinuous(true); if (failFast) { lock.lock(); try { notEmpty.signalAll(); } finally { lock.unlock(); } } 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; } 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 } } }
This is a multi-threaded class, and the unlimited for loop for (;) is set in the run method {}, and the log reports the wrong location information:
connection = createPhysicalConnection();
If the conditions are met, errorcount > connectionerrorretryattempts & & timebetweenconnecterrormillis > 0 will try to reconnect again. Let's see the meaning of these parameters first:
errorCount number of errors
When the run method is initialized, it is zero. Each time the connection fails, it will automatically add 1
connectionErrorRetryAttempts
Number of connection error retries. The default value is 1.
protected int connectionErrorRetryAttempts = 1;
timeBetweenConnectErrorMillis
Connection interval, in milliseconds. The default value is 500.
protected volatile long timeBetweenConnectErrorMillis = DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS; public static final long DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS = 500;
After the connection is interrupted, we have a break in the database
if (breakAfterAcquireFailure) { break; }
Set break after acquire failure to true in application The properties file is configured as follows:
spring.datasource.druid.break-after-acquire-failure=true
If you want to connect more times, you need to set connection error retry attempts. When errorCount is greater than connection errorretryattempts, it will enter the condition and interrupt the cycle. In application The properties file is configured as follows:
spring.datasource.druid.connection-error-retry-attempts=3
summary
The reason why the druid database connection fails is that an unlimited circular connection is used when using multithreading to connect data. The connection needs to be interrupted when the connection fails. You need to set break after acquire failure to true. After setting, the database connection is unsuccessful and will not be retried continuously. If you want to set the number of reconnections, set connection error retry attempts.
If you think the article is helpful to you, please praise it!