ホームページ  >  記事  >  Java  >  springboot druid データベース接続プールが接続失敗後に再接続し続ける問題を解決する方法

springboot druid データベース接続プールが接続失敗後に再接続し続ける問題を解決する方法

王林
王林転載
2023-05-21 11:28:253111ブラウズ

個人用の Alibaba Cloud テスト マシンを使用したときに、リアルタイム出力ログを表示したところ、データベース接続が失敗した後、サーバーが再接続を試行し続けていることがわかりました。当初はシステムが継続的に攻撃を受けていると考えられましたが、サービスを再開したところ、継続的な再接続は発生しなくなりました。次の出力ログを確認してください:

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: ユーザー ' のアクセスが拒否されました。 root' @'113.90.123.76' (パスワードを使用: YES)
at com.mysql.cj.jdbc.Exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.16. jar: 8.0.16]
com.mysql.cj.jdbc.Exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.16.jar:8.0.16]
com.mysql.cj.jdbc.Exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.16.jar:8.0.16]
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]
com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:240) ~[mysql-connector] -java -8.0.16.jar:8.0.16]
com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:199) ~[mysql-connector-java-8.0.16.jar:8.0] .16 ]
com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl.java:156) ~[druid-1.1.10.jar:1.1.10]
com.alibaba.druid.filter .stat .StatFilter.connection_connect(StatFilter.java:218) ~[druid-1.1.10.jar:1.1.10]
com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl.java:150) ~ [druid -1.1.10.jar:1.1.10]
com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1560) ~[druid-1.1.10.jar:1.1.10]
com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1623) ~[druid-1.1.10.jar:1.1.10]
com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread .run (DruidDataSource.java:2468) ~[druid-1.1.10.jar:1.1.10]

上記の druid データベース接続プールのプロンプトが常に表示されることに注意してください。それは druid 接続プールの問題である可能性があり、druid Maven 依存関係を削除した後は、リクエスト インターフェイスでの再接続の問題は発生しません。

Druid 再接続の理由

上記のソース コードの最後の行 DruidDataSource.java:2468 を見つけて、CreateConnectionThread で接続スレッドを作成し、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) {
                        // 必须存在线程等待,才创建连接
                        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
            }
        }
    }

これはマルチスレッド クラスであり、run メソッドには (;;) {} の無制限の for ループとログ エラーの場所情報があります:

connection = createPhysicalConnection();

If 条件は次のとおりです。 met errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0 will tr​​y to reconnect again. まずこれらのパラメータの意味を見てみましょう:

errorCount エラーの数

は、run メソッドが実行されたときは 0 です。接続が失敗するたびに、自動的に 1

connectionErrorRetryAttempts

接続エラーの再試行回数が増加します。デフォルト値は 1 です。

protected int  connectionErrorRetryAttempts  = 1;

timeBetweenConnectErrorMillis

接続間隔時間 (ミリ秒単位)。デフォルト値は 500 です。

protected volatile long timeBetweenConnectErrorMillis = DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS;
public static final long DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS = 500;

データベースへの接続に失敗した後、内部に侵入する必要があります。

if (breakAfterAcquireFailure) {
     break;
}

Break-after-acquire-failure を true に設定し、application.properties ファイルを次のように構成します。

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

複数回接続を試みたい場合は、connection-error-retry-attempts を設定する必要があります。errorCount が connectionErrorRetryAttempts より大きい場合、条件に入り、ループが中断されます。 application.properties ファイルの構成は次のとおりです:

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

以上がspringboot druid データベース接続プールが接続失敗後に再接続し続ける問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。