Home  >  Article  >  Java  >  How to solve the problem that springboot druid database connection pool keeps reconnecting after connection failure

How to solve the problem that springboot druid database connection pool keeps reconnecting after connection failure

王林
王林forward
2023-05-21 11:28:253111browse

When I used my personal Alibaba Cloud test machine, when viewing the real-time output log, I found that after the database connection failed, the server kept trying to reconnect. At first it was thought that the system was under continuous attack, but after restarting the service, the constant reconnection no longer occurred. Look at 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.7e51f00a783d7eb8f68358439dee7daf(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 there is always a prompt for the druid database connection pool above. Here I think it may be the druid connection pool. problem, and then after removing the druid maven dependency, there will be no reconnection problem in the request interface.

Druid reconnection reason

Find the last line DruidDataSource.java:2468 in the source code above. Locate the source code, CreateConnectionThread creates a connection thread, take a look at the CreateConnectionThread source code:

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) {
                        // 必须存在线程等待,才创建连接
                        if (poolingCount >= notEmptyWaitThreadCount //
                                && !(keepAlive && activeCount + poolingCount < minIdle)) {
                            empty.await();
                        }

                        // 防止创建超过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 run method has an unlimited for loop for (;;) {}, and the log error location information:

connection = createPhysicalConnection();

If the conditions are met errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0 will try to reconnect again. Let’s take a look at the meaning of these parameters first:

errorCount number of errors

is zero when the run method is initialized. Each time the connection fails, it will automatically increase 1

connectionErrorRetryAttempts

The number of connection error retries, the default value is 1.

protected int  connectionErrorRetryAttempts  = 1;

timeBetweenConnectErrorMillis

Connection interval time, 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 we fail to connect to the database, we need to break inside.

if (breakAfterAcquireFailure) {
     break;
}

Set break-after-acquire-failure to true, and configure the application.properties file as follows:

spring.datasource.druid.break-after-acquire-failure=true

If you want to try to connect several times more, you need to set connection-error-retry-attempts. When errorCount is greater than connectionErrorRetryAttempts, it will enter the condition and the loop will be interrupted. The following is the configuration of the application.properties file:

spring.datasource.druid.connection-error-retry-attempts=3

The above is the detailed content of How to solve the problem that springboot druid database connection pool keeps reconnecting after connection failure. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete