使用 Spring 的 Tomcat 数据源池中的所有连接都处于活动状态

Posted

技术标签:

【中文标题】使用 Spring 的 Tomcat 数据源池中的所有连接都处于活动状态【英文标题】:All connections are active in Tomcat datasource pool with Spring 【发布时间】:2018-05-19 12:20:22 【问题描述】:

我的 Spring Boot 应用程序使用 JDBCTemplate 将 SQL 查询发送到 PostgreSQL 数据库。似乎每次模板从池中获取连接时,连接都不会被释放。活动连接数 (datasource.primary.active) 一直在增加。

在日志中,使用 JDBCTemplate 进行 SQL 查询后,我可以看到:

DEBUG o.s.j.d.DataSourceUtils - Returning JDBC Connection to DataSource

但是空闲连接数保持不变,活动连接数没有减少。当达到最大值时,将无法检索连接以执行查询。

所以,我认为没有返回到数据源池的连接,请知道吗?

这是使用 Actuator 获得的数据源配置:

  "dataSource": 
        "prefix": "spring.datasource.tomcat",
        "properties": 
            "connectionProperties": null,
            "propagateInterruptState": false,
            "validator": null,
            "useDisposableConnectionFacade": true,
            "defaultCatalog": null,
            "validationInterval": 3000,
            "jmxEnabled": true,
            "ignoreExceptionOnPreLoad": false,
            "logAbandoned": false,
            "commitOnReturn": false,
            "password": "******",
            "maxIdle": 100,
            "testWhileIdle": false,
            "removeAbandoned": false,
            "poolProperties": 
                "dbProperties": 
                    "user": "postgres",
                    "password": "******"
                ,
                "url": "jdbc:postgresql://localhost:5432/tvir",
                "driverClassName": "org.postgresql.Driver",
                "defaultAutoCommit": null,
                "defaultReadOnly": null,
                "defaultTransactionIsolation": -1,
                "defaultCatalog": null,
                "connectionProperties": null,
                "initialSize": 10,
                "maxActive": 100,
                "maxIdle": 100,
                "minIdle": 10,
                "maxWait": 30000,
                "validationQuery": "SELECT 1",
                "validationQueryTimeout": -1,
                "validatorClassName": null,
                "validator": null,
                "testOnBorrow": true,
                "testOnReturn": false,
                "testWhileIdle": false,
                "timeBetweenEvictionRunsMillis": 5000,
                "numTestsPerEvictionRun": 0,
                "minEvictableIdleTimeMillis": 60000,
                "accessToUnderlyingConnectionAllowed": true,
                "removeAbandoned": false,
                "removeAbandonedTimeout": 60,
                "logAbandoned": false,
                "name": "Tomcat Connection Pool[1-574817798]",
                "password": "******",
                "username": "postgres",
                "validationInterval": 3000,
                "jmxEnabled": true,
                "initSQL": null,
                "testOnConnect": false,
                "jdbcInterceptors": null,
                "fairQueue": true,
                "useEquals": true,
                "abandonWhenPercentageFull": 0,
                "maxAge": 0,
                "useLock": false,
                "suspectTimeout": 0,
                "dataSource": null,
                "dataSourceJNDI": null,
                "alternateUsernameAllowed": false,
                "commitOnReturn": false,
                "rollbackOnReturn": false,
                "useDisposableConnectionFacade": true,
                "logValidationErrors": false,
                "propagateInterruptState": false,
                "ignoreExceptionOnPreLoad": false,
                "useStatementFacade": true
            ,

以及用于查询数据库的代码:

JdbcTemplate jdbcTemplate = appCtx.getBean(JdbcTemplate.class);

ResultSet columns = jdbcTemplate.getDataSource().getConnection().getMetaData().getColumns(null, null, source.getTable().toLowerCase(), null);

String selectList = "";


while (columns.next())


    String colName = columns.getString("COLUMN_NAME");
    String colType = columns.getString("DATA_TYPE");


    if(!selectList.equals("")) 
        selectList += ", ";
    


    if((""+java.sql.Types.INTEGER).equalsIgnoreCase(colType) || 
            (""+java.sql.Types.DOUBLE).equalsIgnoreCase(colType) ||
            (""+java.sql.Types.BIGINT).equalsIgnoreCase(colType) ||
            (""+java.sql.Types.FLOAT).equalsIgnoreCase(colType) ) 
        selectList += "SUM(" + colName + ")";
     else 
        selectList += "MAX(" + colName + ")";
    

    selectList += " AS "+colName;


            String sql = "SELECT "+selectList+" FROM "+source.getTable()+" "+
                            "WHERE "+source.getDateColumn()+" >= ? "+
                            "AND "+source.getDateColumn()+" <= ? ";


List<Map<String, Object>> results = jdbcTemplate.queryForList(sql, Date.valueOf(startDate), Date.valueOf(endDate));

【问题讨论】:

这通常意味着您要么有错误的事务设置,要么在 spring 范围之外自己弄乱了连接。 我使用的是默认事务模式,这是来自数据源的参数:"defaultTransactionIsolation": -1 并且请求是通过jdbcTemplate.queryForList()完成的 那么你自己在 Spring 范围之外搞乱了连接。为您的问题添加一些代码和配置。 我已经添加了 conf 和 code。可能它与元数据部分相关联?我可以使用这种方式获得连接吗?如何在没有连接的情况下获取元数据? 不,你不能也不应该因为这是连接泄漏(改用ConnectionCallback),还有一点可疑(至少在我的书中)是你这样做appCtx.getBean(JdbcTemplate.class)你应该使用依赖注入而不是自己从上下文中获取bean。 【参考方案1】:

Spring boot 允许您配置您希望数据源的行为方式。 您可以在official doc 上找到完整列表。 检查您的案例的以下属性:

spring.datasource.maxActive
spring.datasource.maxIdle

根据您使用的连接池,您还可以使用 Spring Boot 属性对其进行调整(一切都在文档中)。

【讨论】:

我使用默认值,所以 maxActive=100maxIdle=10 。此配置中的任何内容都可以解释为什么连接没有回到空闲模式?

以上是关于使用 Spring 的 Tomcat 数据源池中的所有连接都处于活动状态的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 应用程序的 tomcat 中的默认线程池

如何增加tomcat线程池中的线程数?

Spring Cloud Gateway中netty线程池优化

org.apache.tomcat.util.bcel.classfile.ClassFormatException:常量池中的无效字节标记:15

Tomcat数据源--DataSource&Connection Pool

如何跟踪/记录 tomcat dbcp 池中的连接并检测不返回连接池的代码