【小知识】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方法

你可能感兴趣的:(javadruid)