乐趣区

关于java:小知识CommunicationsException异常

问题

线上在查比较复杂的 SQL 是,呈现以下报错:

Error querying database.  Cause: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure\n\nThe last packet successfully received from the server was 10,181 milliseconds ago. The last packet sent successfully to the server was 10,239 milliseconds ago.

从报错意思上看,是数据库连贯曾经被敞开了。通过屡次试验确认,只有 SQL 执行超过 10s,就会呈现以上谬误。

起因

我的项目是springboot,数据库连接池用的是阿里的druid-spring-boot-starter-1.2.15

如上图所示:JDBC 通过 socket 对字节流进行解决,因而也会有一些根本网络操作,相似于 HttpClient 这种用于网络操作的代码库;同样的也会受到 ConnectTimeout/SocketTime 的影响。
DruidDataSource有设置以上超时的办法:

DruidDataSource dataSource = new DruidDataSource();
dataSource.setConnectTimeout(100_000);
dataSource.setSocketTimeout(100_000);

然而以后零碎并没有设置以上参数,那为什么申请超时 10s 就会报错?并且以后 druid-spring-boot-starter-1.2.15 版本并没有 socketTime&connetctTimeout 配置参数(无奈通过 spring.datasource.druid 前缀来配置)。
通过源码(DruidDatasource 类)能够看到以下内容:

public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource, MBeanRegistration {public void init() throws SQLException {
        …… // 省略
        try {
            …… // 省略
            if (this.jdbcUrl != null) {this.jdbcUrl = this.jdbcUrl.trim();
                …… // 省略
                initFromUrlOrProperties(); // 从 url 里解析参数}
            if (connectTimeout == 0) {socketTimeout = DEFAULT_TIME_CONNECT_TIMEOUT_MILLIS; // 10_000}
            if (socketTimeout == 0) {socketTimeout = DEFAULT_TIME_SOCKET_TIMEOUT_MILLIS; // 10_000}
            …… // 省略
        } catch (SQLException e) {…… // 省略} finally {…… // 省略}
    }
}

通过以上源码能够看到,如果没有设置参数值,则会默认设置为 10s
持续看 initFromUrlOrProperties 办法:

public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource, MBeanRegistration {private void initFromUrlOrProperties() {
        // 从 jdbc 的 url 里解析   
        if (jdbcUrl.startsWith("jdbc:mysql://")) {if (jdbcUrl.indexOf("connectTimeout=") != -1 || jdbcUrl.indexOf("socketTimeout=") != -1) {String[] items = jdbcUrl.split("(\\?|&)");
                for (int i = 0; i < items.length; i++) {String item = items[i];
                    if (item.startsWith("connectTimeout=")) {String strVal = item.substring("connectTimeout=".length());
                        setConnectTimeout(strVal);
                    } else if (item.startsWith("socketTimeout=")) {String strVal = item.substring("socketTimeout=".length());
                        setSocketTimeout(strVal);
                    }
                }
            }
            // 从配置的 connectProperties 里解析:spring.datasource.druid.connectionProperties=connectTimeout=50000;socketTimeout=50000;
            Object propertyConnectTimeout = connectProperties.get("connectTimeout");
            if (propertyConnectTimeout instanceof String) {setConnectTimeout((String) propertyConnectTimeout);
            } else if (propertyConnectTimeout instanceof Number) {setConnectTimeout(((Number) propertyConnectTimeout).intValue());
            }

            Object propertySocketTimeout = connectProperties.get("socketTimeout");
            if (propertySocketTimeout instanceof String) {setSocketTimeout((String) propertySocketTimeout);
            } else if (propertySocketTimeout instanceof Number) {setSocketTimeout(((Number) propertySocketTimeout).intValue());
            }
        }
    }    
}

能够看到有以下两种形式来配置:

  • JDBC 的 URL:jdbc:mysql://xxxx?connectTimeout=50000&socketTimeout=50000
  • 通过配置 spring.datasource.druid.connectionProperties 来设置:spring.datasource.druid.connectionProperties=connectTimeout=50000;socketTimeout=50000;

并且因为 initFromUrlOrProperties 办法是在 init 办法内,以及 propertyConnectTimeout 是在 URL 前面解析,能够得出优先级:connectProperties 大于 jdbcUrl 大于 set 办法

退出移动版