Spring Boot統合Druidに異常エラーが発生した原因と解決


Spring Boot統合Druid異常
Spring Boot統合Druidプロジェクトにおいて、エラーログには以下のようなエラーメッセージが頻繁に表示されていることが分かりました。

discard long time none received connection. , jdbcUrl : jdbc:mysql://******?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8, version : 1.2.3, lastPacketReceivedIdleMillis : 172675
調査の結果、ドライドバージョンによる異常は1.2.2及び以前のバージョンでは発生していませんでした。これらのバージョンにはこの問題があります。異常原因と解決策を分析します。
異常分析
まず上の異常はプログラムの正常な運行に影響しませんが、プログラマーとしてはプログラムに異常があることを見て耐えられません。だから、根掘り葉掘り聞いて解決します。
スタック情報を追跡すると、対応の異常がcomp.alibababa.druid.pool.DruidAbstract Data Source咻testConnection Internalメソッドから投げられたもので、対応するコードは以下の通りです。

if (valid && isMySql) { // unexcepted branch
    long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);
    if (lastPacketReceivedTimeMs > 0) {
        long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;
        if (lastPacketReceivedTimeMs > 0 //
                && mysqlIdleMillis >= timeBetweenEvictionRunsMillis) {
            discardConnection(holder);
            String errorMsg = "discard long time none received connection. "
                    + ", jdbcUrl : " + jdbcUrl
                    + ", jdbcUrl : " + jdbcUrl
                    + ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis;
            LOG.error(errorMsg);
            return false;
        }
    }
}
上記のコードの中で、MySQlUtils.getLastPacketReceivedTimeMsは前回使用した時間を取得します。mysql IdleMillisは暇な時間を計算します。timeBetweenEnitionRunsは定数60秒です。接続が60秒以上空きましたら、discardConnectionはこの古い接続を破棄してログLOG.warnを印刷しました。
原理追跡
上記のコードの中で、私達はこの業務のロジックに入るのが前提条件があるので、つまりvalidとisMySql変数は同時にtrueです。isMySqlはtrueのために必要です。私たちが使っているのはMysqlデータベースです。では、validをfalseにしてもいいですか?このようにすれば、この業務に入ることができなくなりますか?
validのソースを見に来ましたか?それともこの方法の上ですか?

boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);

私たちはvalidConnection CheckerのMysql実現サブクラスMySql ValidConneckerを見つけました。このクラスの中でisValidConnectionの実現は以下の通りです。

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);
    }

}
上記の方法には3つの戻りが見られます。最初の接続がオフになりました。二つ目はpingの形式を使って検査を行います。第三に、select 1の方式で検査を行います。pingの形式で検査すると、異常を投げてもtrueに戻ります。ここではこのモードを無効にすればいいです。
pingに入る業務ロジックは主に変数usePingMethodによって判断されます。追跡コードはここで行われている設定を発見します。

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);
    }
}
つまり、システム属性druid.mysql.use PingMethodをfalseに設定すれば、この機能を無効にできます。
Ping Methodを無効にします
問題の根源を見つけたら、残りはどうやって無効にしますか?
第一に、プログラムを起動する時に運転パラメータの中で増加します。-Ddruid.mysql.use PingMethod=false。
第二に、Spring Bootプロジェクトでは、以下のような静的なコードを起動クラスに追加することができます。

static {
    System.setProperty("druid.mysql.usePingMethod","false");
}
第三に、クラスファイルの構成。プロジェクトのDruidConfig類に新たに追加されました。

/*
*   druid     :discard long time none received connection:xxx
* */
@PostConstruct
public void setProperties(){
    System.setProperty("druid.mysql.usePingMethod","false");
}
これで、この機能を無効にすることができます。異常情報はもう現れません。
なぜ空き時間が60秒以上あるかを確認します。
推測では、アリがデータベースに設定したデータベースの空き待ち時間は60秒で、mysqlデータベースが空き時間になると空き時間の接続をオフにして、データベースサーバの処理能力を向上させます。
MySQLのデフォルトの空き時間は8時間で、「wait_」です。timeout」の設定値です。データベースが空いている接続を自動的にオフにすると、接続池が分かりません。まだこの接続を使っていると、異常が発生します。
以上がSpring Boot統合Druidに異常が発生した原因と解決の詳細です。Spring Boot統合Druidに異常が発生した資料について、他の関連記事に注目してください。