连接泄漏导致java.sql.SQLNonTransientConnectionException

问题描述 投票:0回答:1

我正在开发一个 ETL 应用程序,应该定期进行。出于测试目的,我将时间段设置为 5 分钟,但在生产中它会更长。我使用 Spring 框架进行调度,并使用 JdbcTemplate 来执行提取和加载步骤。现在,我的笔记本电脑上的 MySQL 数据库中拥有所有内容,但最终将有两个独立的数据库(MySQL 和 DB2)用于提取,Snowflake 用于加载。我创建了以下课程:

public class SqlDataConnector implements DataConnector {

    private final JdbcTemplate jdbc;
    private List<String> keys = null;

    public SqlDataConnector(Map<String, String> attributes, String driver) {
        DataSource ds = createDataSource(attributes, driver);
        jdbc = new JdbcTemplate(ds);
    }

    @Override
    public List<Map<String, Object>> getData(String query) {
        return jdbc.queryForList(expression);
    }

    @Override
    public void close() {
        if (jdbc != null) {
            DataSource dataSource = jdbc.getDataSource();
            if (dataSource != null) {
                Connection connection = null;
                try {
                    connection = dataSource.getConnection();
                } catch (SQLException se) {
                } finally {
                    if (connection != null) {
                        try {
                            connection.close();
                        } catch (SQLException ex) {
                        }
                    }
                }
            }
        }
    }
    ....
}

每一步后我都会关闭连接。像这样:

private List<Map<String, Object>> performExtraction() {
    DataConnector connector = connectorFactory.getConnector(task.getConnectors().get(0));
    List<Map<String, Object>> data = connector.getData(task.getExpression());
    connector.close();
    return data;
}

我的代码按预期工作,但是 4 次迭代一切正常。在第五个开始时,我遇到以下异常:

2024-03-02 21:27:36.169 [ETL] [pool-6-thread-1] [错误] com.zaxxer.hikari.pool.HikariPool:HikariPool-17 - 池初始化期间出现异常。 java.sql.SQLNonTransientConnectionException:数据源拒绝建立连接,来自服务器的消息:“连接过多” 在 com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:110) 在com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) 在 com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:828) 在 com.mysql.cj.jdbc.ConnectionImpl.(ConnectionImpl.java:448) 在 com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:241) 在com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198) 在com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138) 在 com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:359) 在 com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:201) 在 com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:470) 在com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:561) 在 com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:100) 在com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112) 在 org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:160) 在org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:118) 在 org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:81) 在 org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:388)

我的连接属性如下:

spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.pool-name="etl-daemon-pool"
spring.datasource.hikari.minimumIdle=2
spring.datasource.hikari.maximumPoolSize=4
spring.datasource.hikari.idleTimeout=30000
spring.datasource.hikari.maxLifetime=2000000
spring.datasource.hikari.connectionTimeout=60000

显然,我在某个地方有连接泄漏,但找不到它在哪里。我确实浏览了几篇关于这个主题的 SO 帖子,并尝试遵循其中的建议,但没有任何效果。我确实放置了日志记录语句,并看到连接已成功关闭。 无法弄清楚为什么这些连接没有被释放。如果有人帮助我解决这个问题,我将不胜感激。

java database-connection hikaricp connection-leaks
1个回答
0
投票

我不知道这是否能解决你的问题,但这里有一个潜在的泄漏:

private List<Map<String, Object>> performExtraction() {
    DataConnector connector = 
         connectorFactory.getConnector(task.getConnectors().get(0));
    List<Map<String, Object>> data = connector.getData(task.getExpression());
    connector.close();
    return data;
}

如果在获取数据时抛出异常,则连接器关闭调用不会发生。你应该这样写:

private List<Map<String, Object>> performExtraction() {
    try (DataConnector connector =
        connectorFactory.getConnector(task.getConnectors().get(0));
    ) {
        List<Map<String, Object>> data = 
            connector.getData(task.getExpression());
        return data;
    }
}

你的

SqlDataConnector
类应该实现
AutoCloseable

© www.soinside.com 2019 - 2024. All rights reserved.