[alibaba/druid]1.1.5 修复连接池某些场景testWhileIdle不起作用的问题

2023-12-09 204 views
0

能具体描述一下是什么场景吗?我现在是使用 版本: mysql-connector-->5.1.34 druid -->1.0.6

配置如下: validationQuery=SELECT 'x'
testWhileIdle=true
testOnBorrow=false
testOnReturn=false

timeBetweenEvictionRunsMillis=25000
minEvictableIdleTimeMillis=300000

removeAbandoned=true
removeAbandonedTimeout=18000
logAbandoned=true

hibernate.show_sql=false
collectionInit=set names utf8mb4

但是偶尔会出现下面的错,感觉像是服务端断开连接,但是我testwhileIdle配置了true,看源码获取连接的时候是会去做ping操作检测连接的.

    ... 57 more                                                                                                                               

Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 300,001 milliseconds ago. The last packet sent successfully to the server was 300,000 m illiseconds ago.

回答

8

遇到了同样的问题 版本
mysql-connector 5.1.38 druid : 1.0.26 配置: jdbc.initialSize=10 jdbc.minIdle=10 jdbc.maxActive=100 jdbc.maxWait=200 jdbc.validationQuery=select 1 jdbc.testOnBorrow=false jdbc.testOnReturn=false jdbc.testWhileIdle=true jdbc.timeBetweenEvictionRunsMillis=60000 jdbc.minEvictableIdleTimeMillis=1800000 jdbc.removeAbandoned=true jdbc.removeAbandonedTimeout=1800 jdbc.logAbandoned=true

偶尔出现以下错误 The last packet successfully received from the server was 55,118 milliseconds ago. The last packet sent successfully to the server was 5,430 milliseconds ago.; nested exception is com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

0

上面的报错 , 55,118 小于timeBetweenEvictionRunsMillis设置的60,000ms 但是 还有 926,928ms的,如下: The last packet successfully received from the server was 926,928 milliseconds ago. The last packet sent successfully to the server was 926,646 milliseconds ago.; nested exception is com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

0

同问,配置了testWhileIdle=true和validationQuery=SELECT 'x',甚至配置了testOnBorrow=true,testOnReturn=true,但是超过mysql默认的的wait_timeout=8小时,数据库连接还是会被强制收回。 版本是1.1.6,是否有人可以解答?谢谢!

5

感觉不应该啊,是不是应用持有连接没使用?

1

大家版本配置不同分开提吧,不要放在一个问题里了, @wenshao,你还需要反馈什么信息?理论上我配置testWhileIdle,获取连接时如果超过timeBetweenEvictionRunsMillis的时间,获取连接时,应该会生新通过ping的方式校验连接才对。但是不清楚为什么,还是会出现实际通信时连接被断开的情况,而且最后发送包时间和接收包时间都是近300,000毫秒.我这里用的hibernate版本是4.3.7.Final

5

druid -->1.1.5 mysql-connector -->5.1.42 spring boot -->1.5.6

mysql协议为com.mysql.jdbc.ReplicationDriver

spring boot配置 spring: datasource: druid: min-idle: 5 initial-size: 10 max-active: 200 test-on-borrow: true test-while-idle: true validation-query: SELECT 1 min-evictable-idle-time-millis: 300000 keep-alive: true

事务提交方式为: @Transactional(readOnly = true)

事务提交readonly=true的时候,会出现testWhileIdle不起作用的问题,报出错误为The last packet successfully received from the server was 1800,586 milliseconds ago.

0

最近也遇到了这个问题,druid:1.1.9,spring的健康检查中拿到了空闲连接,虽然配置了testWhileIdle为true,但还是抛出了discard connection异常……

9

我这边问题解决了。我发现是使用了TIDB的缘故,同样的数据,连接Mysql不会出现问题,连TIDB会有这样的问题。不论怎么修改参数都不管用

4

同样的问题:使用mybatis + druid-1.1.9 以下是mysql的配置和项目的配置 image image

6

JdbcConstants类的常量MYSQL_DRIVER_REPLICATE是不是有问题,少写了ReplicationDriver

String MYSQL_DRIVER_REPLICATE = "com.mysql.jdbc.";

JdbcUtils类里判断是否是MySQL驱动的方法,用ReplicationDriver就有问题了 public static boolean isMySqlDriver(String driverClassName) { return driverClassName.equals(JdbcConstants.MYSQL_DRIVER) // || driverClassName.equals(JdbcConstants.MYSQL_DRIVER_6) || driverClassName.equals(JdbcConstants.MYSQL_DRIVER_REPLICATE); }

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

            // 当前的conn 是否是  com.mysql.jdbc.MySQLConnection (or com.mysql.cj.jdbc.ConnectionImpl)
            if (clazz.isAssignableFrom(conn.getClass())) {
                if (validationQueryTimeout < 0) {
                    validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;
                }

                //使用反射调用MySQLConnection.pingInternal 方法,检查连接有效性,并且会刷新连接的空闲时间
                // 如果失败则会抛出异常,上层捕获
                ping.invoke(conn, true, validationQueryTimeout * 1000); 
                return true;
            }
        }

        // 当usePingMethod=false或者 conn 不是 com.mysql.jdbc.MySQLConnection (or com.mysql.cj.jdbc.ConnectionImpl) 会执行一下方法
        if (validateQuery == null || validateQuery.length() == 0) {
            return true;
        }

        Statement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.createStatement();
            if (validationQueryTimeout > 0) {
                stmt.setQueryTimeout(validationQueryTimeout);
            }
            // 执行 select 1 ,并且会刷新连接的空闲时间
            //  如果失败则会抛出异常,上层捕获
            rs = stmt.executeQuery(validateQuery);
            return true;
        } finally {
            JdbcUtils.close(rs);
            JdbcUtils.close(stmt);
        }
    }
  • 通过调试,发现clazz.isAssignableFrom(conn.getClass())为false,也就是说这里的conn并不是com.mysql.jdbc.MySQLConnection,原来为了DB的读写分离项目使用的是数据库驱动是RepliationDriver而不是默认的Driver(jdbc:mysql:replication://), 因此使用的连接也是com.mysql.jdbc.ReplicationConnection, 而ReplicationConnection直接继承自 com.mysql.jdbc.Connection 并没有继承com.mysql.jdbc.MySQLConnection(仅限于mysql-connection-java 1.5.38版本之前)

  • 通过debug发现 druid 的检查还是会正常走到,当走到 ReplicationConnection 内部的时候 ReplicationConnection 有一个 currentConnection ,这个链接是会在 masterConnection 和 slaveConnection 之间切换,切换的依据是 readOnly 参数。

  • 在检查的时候由于 druid 并不感知上层的参数,readOnly 也就不会设置。所以走的是 masterConnection ,但是在程序里用的时候通过 spring 的 TransactionManager 将 readOnly 传播到了 ShardingJDBC , ShardingJDBC 在设置到 ReplicationConnection 上,最后导致真正在使用的时候其实使用的是 slaveConnection。

8

1.0.27 也有此问题, 升级到1.1.20就好了

5

老哥,我也用的tidb,是咋解决的,看了好多没什么头绪