druid报错 discard long time none received connection

这篇具有很好参考价值的文章主要介绍了druid报错 discard long time none received connection。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

问题背景

在项目启动时出现大量
druid报错 discard long time none received connection,数据库,mysql,java
c.a.d.pool.DruidAbstractDataSource: discard long time none received connection.
明显是Druid管理的数据库连接因为太长时间没有收到数据库发来的数据,把连接给回收掉了,这导致服务在启动时因为要重复创建连接让服务启动时间延长。

定位原因

根据错误信息,找到Druid源码
com.alibaba.druid.pool.DruidAbstractDataSource#testConnectionInternal(com.alibaba.druid.pool.DruidConnectionHolder, java.sql.Connection)

if (validConnectionChecker != null) {
	// 验证连接的有效性 mysql下实际调用代码在下面那块
	boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);
	long currentTimeMillis = System.currentTimeMillis();
    if (holder != null) {
        holder.lastValidTimeMillis = currentTimeMillis;
        holder.lastExecTimeMillis = currentTimeMillis;
    }

    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
                        + ", version : " + VERSION.getVersionNumber()
                        + ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis;
                LOG.warn(errorMsg);
                return false;
            }
        }
    }
    // ... 省略
}


// com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker#isValidConnection
    public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
        if (conn.isClosed()) {
            return false;
        }

        if (usePingMethod) {
        	// 以ping的方式检测连接的有效性
            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()) {
        	// 以 sql SELECT 1 的方式验证连接有效性
            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);
        }

    }
}

这是调用 testConnectionInternal方法的上层.
druid报错 discard long time none received connection,数据库,mysql,java
可以看到,因为我们开启了testOnBorrow 开关,所以数据库连接会在申请成功后,立即进行一次测试,然后根据数据库连接的最后一次心跳时间,判断是否闲置过长要丢弃掉该数据库连接。
该开关主要在从连接池获取时立即检查连接的有效性。
而不开启testOnBorrow则会在保持连接过程中不断检查连接的闲置情况,对闲置过长的连接回收。

com.alibaba.druid.util.MySqlUtils#getLastPacketReceivedTimeMs 这个方法会返回连接最后一次收到消息的时间.

// 以mysql6的 com.mysql.cj.jdbc.ConnectionImpl 为栗子
// getLastPacketReceivedTimeMs 方法中获取链接时间的实际方法
public long getIdleFor() {
     return this.lastQueryFinishedTime == 0 ? 0 : System.currentTimeMillis() - this.lastQueryFinishedTime;
 }

// com.mysql.cj.NativeSession#execSQL
    public <T extends Resultset> T execSQL(Query callingQuery, String query, int maxRows, NativePacketPayload packet, boolean streamResults,
            ProtocolEntityFactory<T, NativePacketPayload> resultSetFactory, ColumnDefinition cachedMetadata, boolean isBatch) {

        long queryStartTime = this.gatherPerfMetrics.getValue() ? System.currentTimeMillis() : 0;
        int endOfQueryPacketPosition = packet != null ? packet.getPosition() : 0;

        this.lastQueryFinishedTime = 0; // we're busy!

        if (this.autoReconnect.getValue() && (getServerSession().isAutoCommit() || this.autoReconnectForPools.getValue()) && this.needsPing && !isBatch) {
            try {
                ping(false, 0);
                this.needsPing = false;

            } catch (Exception Ex) {
                invokeReconnectListeners();
            }
        }

        try {
            return packet == null
                    ? ((NativeProtocol) this.protocol).sendQueryString(callingQuery, query, this.characterEncoding.getValue(), maxRows, streamResults,
                            cachedMetadata, resultSetFactory)
                    : ((NativeProtocol) this.protocol).sendQueryPacket(callingQuery, packet, maxRows, streamResults, cachedMetadata, resultSetFactory);

        } catch (CJException sqlE) {
            if (getPropertySet().getBooleanProperty(PropertyKey.dumpQueriesOnException).getValue()) {
                String extractedSql = NativePacketPayload.extractSqlFromPacket(query, packet, endOfQueryPacketPosition,
                        getPropertySet().getIntegerProperty(PropertyKey.maxQuerySizeToLog).getValue());
                StringBuilder messageBuf = new StringBuilder(extractedSql.length() + 32);
                messageBuf.append("\n\nQuery being executed when exception was thrown:\n");
                messageBuf.append(extractedSql);
                messageBuf.append("\n\n");
                sqlE.appendMessage(messageBuf.toString());
            }

            if ((this.autoReconnect.getValue())) {
                if (sqlE instanceof CJCommunicationsException) {
                    // IO may be dirty or damaged beyond repair, force close it.
                    this.protocol.getSocketConnection().forceClose();
                }
                this.needsPing = true;
            } else if (sqlE instanceof CJCommunicationsException) {
                invokeCleanupListeners(sqlE);
            }
            throw sqlE;

        } catch (Throwable ex) {
            if (this.autoReconnect.getValue()) {
                if (ex instanceof IOException) {
                    // IO may be dirty or damaged beyond repair, force close it.
                    this.protocol.getSocketConnection().forceClose();
                } else if (ex instanceof IOException) {
                    invokeCleanupListeners(ex);
                }
                this.needsPing = true;
            }
            throw ExceptionFactory.createException(ex.getMessage(), ex, this.exceptionInterceptor);

        } finally {
        	// 需要开启数据库连接的jdbc参数 maintainTimeStats=true
            if (this.maintainTimeStats.getValue()) {
            	// 连接的最后查询时间被更新
                this.lastQueryFinishedTime = System.currentTimeMillis();
            }

            if (this.gatherPerfMetrics.getValue()) {
                ((NativeProtocol) this.protocol).getMetricsHolder().registerQueryExecutionTime(System.currentTimeMillis() - queryStartTime);
            }
        }
    }

解决

通过源码分析,就大致清楚问题的原因。
druid会从数据库获取一批连接持有在本地,以便快速使用。
为了检查连接的可用(如连接超时被数据库回收了,网络异常等),所以当开启testOnBorrow开关后,会在客户端从druid获取连接时进行闲置连接检查。
而闲置检查时比较连接当前时间与最后一次执行sql的时间的差值。
我们的服务在启动时没有进行数据查询,并且连接保活维持是通过ping的方式,所以当启动时间超过之前设置的15s后,再使用最开始池化的数据库借入连接时检测不过而抛出文章开头的异常信息。

我们可以通过调大闲置连接剔除时间和保活时间,让连接闲置能够撑过服务启动的无数据查询时间。
此外,如果服务的活跃情况很低,也就是执行sql的频率很低,可以设置环境变量druid.mysql.usePingMethodfalse,让druid以执行SELECT 1sql的方式来保活连接,如此就会顺带刷新getLastPacketReceivedTimeMs属性。

// com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker#configFromProperties
    public void configFromProperties(Properties properties) {
        if (properties == null) {
            return;
        }

        String property = properties.getProperty("druid.mysql.usePingMethod");
        if ("true".equals(property)) {
            setUsePingMethod(true);
        } else if ("false".equals(property)) {
            setUsePingMethod(false);
        }
    }

当然通过源码还有其他方式,可以自行发现。文章来源地址https://www.toymoban.com/news/detail-554793.html

spring:
	datasource:
		druid:
			# 让底层的jdbc维护连接的状态的时间
			url: jdck:mysql://xxx?maintainTimeStats=true
			# 连接闲置剔除时间
      		time-between-eviction-runs-millis: 300000
      		# 必须大于 time-between-eviction-runs-millis 时间
      		keep-alive-between-time-millis: 450000
	// 启动代码添加系统属性
	// 或者通过 -Ddruid.mysql.usePingMethod=false 的命令参数
	// 或者通过环境变量
    public static void main(String[] args) {
        Properties properties = System.getProperties();
        // 用 select 1 替换 ping 来检测连接保活
        properties.setProperty("druid.mysql.usePingMethod", "false");

        SpringApplication.run(App.class, args);
    }

到了这里,关于druid报错 discard long time none received connection的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 安装MySQl报Initializing database (may take a long time)

    mysql安装卡在initializing database这一步报错 查看日志提示里面有中文,无法找到file,最终查出乱码部分是计算机名和计算机组名 解决办法:右键我的电脑属性–查看自己的计算机名和组名是否为中文–更改计算机名和组名–重启计算机–使用MySQL Installer安装程序安装MySQL serv

    2024年02月07日
    浏览(39)
  • 时间序列(Time-Series)exp_long_term_forecasting.py代码解析

    from data_provider.data_factory import data_provider from exp.exp_basic import Exp_Basic from utils.tools import EarlyStopping, adjust_learning_rate, visual from utils.metrics import metric import torch import torch.nn as nn from torch import optim import os import time import warnings import numpy as np warnings.filterwarnings(\\\'ignore\\\') #长期预测类 cla

    2024年01月22日
    浏览(52)
  • 【论文阅读】Informer Beyond Efficient Transformer for Long Sequence Time-Series Forecasting

    原始题目:Informer: Beyond Efficient Transformer for Long Sequence Time-Series Forecasting 中文翻译:Informer:超越有效变换器进行长序列时间序列预测 发表时间:2021-05-18 平台:Proceedings of the AAAI Conference on Artificial Intelligence 文章链接:https://ojs.aaai.org/index.php/AAAI/article/view/17325 开源代码:htt

    2024年03月12日
    浏览(54)
  • SocketTimeoutException:Read timed out问题解决org.apache.cxf.interceptor.Fault:Could not receive Message

    利用WebService给其它服务器地址发送请求时,遇到了一个报错: org.apache.cxf.interceptor.Fault:Could not receive Message。 控制台上显示: java.net.SocketTimeoutException:Read timed out。 搜了一下发现网上大部分是分享 org.apache.cxf.interceptor.Fault:Could not send Message。 发送问题的,很少有关于接收报错

    2024年02月05日
    浏览(44)
  • 论文笔记:A Time Series is Worth 64 Words: Long-term Forecasting with Transformers

    ICLR 2023 比较简单,就不分intro、model这些了 给定每个时间段的长度、划分的stride,将时间序列分成若干个时间段 时间段之间可以有重叠,也可以没有 每一个时间段视为一个token 降低复杂度 Attention 的复杂度是和 token 数量成二次方关系。 如果每一个 patch 代表一个 token,而不是

    2024年02月07日
    浏览(47)
  • 【flutter doctor HTTP host常见报错】HTTP Host availability check is taking a long time...

    HTTP Host availability check is taking a long time…[!] HTTP Host Availability HTTP host “https://maven.google.com/” is not reachable. Reason: An error occurred while checking the HTTP host: 信号灯超时时间已到 1、找到flutter sdk的文件目录,依次找到flutter/packages/flutter_tools/lib/src/http_host_validator.dart文件 2、将https://

    2024年02月12日
    浏览(49)
  • 【问题记录】git报错:[remote rejected] (pre-receive hook declined)

    经过排查,不是因为权限不足,是由于commit提交不规范引起的提交失败,报错如下 修改commit信息,用下面这行命令就行 然后重新push,就成功了

    2024年02月12日
    浏览(60)
  • idea 启动报错 Command line is too long

    启动报错信息: Error running ‘Application‘: Command line is too long. 翻译过来就是:启动命令过长! 解决方案 1、点开项目启动配置项目; 2、 shorten command line 选项选择 JAR manifest 或者 classpath file 选项,点击 Apply 和 OK,然后重启项目; Error running test: Command line is too long 上面是运行

    2024年02月10日
    浏览(54)
  • [Selenium]Message: disconnected: Unable to receive message from rendererXXX:报错解决

    selenium.common.exceptions.WebDriverException: Message: disconnected: Unable to receive message from renderer   (failed to check if window was closed: disconnected: not connected to DevTools)   (Session info: MicrosoftEdge=115.0.xxxx.xxx)         示例中使用的是最新版本的 webdriver.EdgeOptions() 创建 edge_options 实例。然后使用

    2024年02月04日
    浏览(46)
  • 解决mysql报错:1406, Data too long for column(多种方案)

    传送 centosAI 适用于linux系统 一般1406, Data too long for column错误大部分会是因为某一列表的数据长度受限,最好的办法是进入数据库,找到表结构中对应的表头,修改数据库类型,这里以vod_first_letter为例,如果直接修改数据类型,可能会因为文本内容不兼容而导致失败,如将V

    2024年01月21日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包