如何修复Grails中的连接池泄漏?

时间:2015-06-11 21:13:21

标签: grails connection-pooling

这是Grails 2.4.4。我遇到了一个似乎与Grails事务管理有关的连接池泄漏。有谁知道问题出在哪里?

来自Tomcat JDBC Pool的可疑泄漏检测消息:

015-06-11 13:44:03,483 [PoolCleaner[2120388162:1434055276957]] [||] WARN  pool.ConnectionPool  - Connection has been marked suspect, possibly abandoned PooledConnection[org.postgresql.jdbc4.Jdbc4Connection@4fcf9353][89657 ms.]:java.lang.Exception
at org.apache.tomcat.jdbc.pool.ConnectionPool.getThreadDump(ConnectionPool.java:1063)
at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:780)
at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:619)
at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:188)
at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:128)
at org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy$LazyConnectionInvocationHandler.getTargetConnection(LazyConnectionDataSourceProxy.java:403)
at org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy$LazyConnectionInvocationHandler.invoke(LazyConnectionDataSourceProxy.java:376)
at com.sun.proxy.$Proxy77.prepareStatement(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy$TransactionAwareInvocationHandler.invoke(TransactionAwareDataSourceProxy.java:240)
at com.sun.proxy.$Proxy77.prepareStatement(Unknown Source)
at groovy.sql.Sql$CreatePreparedStatementCommand.execute(Sql.java:4512)
at groovy.sql.Sql$CreatePreparedStatementCommand.execute(Sql.java:4491)
at groovy.sql.Sql.getAbstractStatement(Sql.java:4342)
at groovy.sql.Sql.getPreparedStatement(Sql.java:4357)
at groovy.sql.Sql.getPreparedStatement(Sql.java:4434)
at groovy.sql.Sql.access$900(Sql.java:228)
at groovy.sql.Sql$PreparedQueryCommand.runQuery(Sql.java:4622)
at groovy.sql.Sql$AbstractQueryCommand.execute(Sql.java:4553)
at groovy.sql.Sql.rows(Sql.java:1954)
at groovy.sql.Sql.firstRow(Sql.java:2192)
at groovy.sql.Sql$firstRow.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
...

在resources.groovy:

import groovy.sql.Sql
beans = {
    sql(Sql, ref("dataSource"));
}

在DataSource.groovy中:

dataSource {
url = "jdbc:postgresql:testdb"
username = "USERNAME"
password = "PASSWORD"
driverClassName = "org.postgresql.Driver"
dialect = "org.hibernate.dialect.PostgreSQL9Dialect"
pooled = true
jmxEnabled = true
readOnly = false
autoCommit = false
properties {
    initialSize = 1
    maxActive = 15
    minIdle = 1
    maxIdle = 2
    maxWait = 20000 // 20 seconds
    maxAge = 20 * 60000 // 20 minutes
    timeBetweenEvictionRunsMillis = 15000
    minEvictableIdleTimeMillis = 10000
    validationQueryTimeout = 10 // 10 seconds
    validationInterval = 15000 // 15 seconds
    testOnBorrow = true
    testWhileIdle = true
    testOnReturn = false
    logValidationErrors = true
    removeAbandonedTimeout = 60 * 1440 // 24 hours, use suspectTimeout instead
    removeAbandoned = true // needed for suspectTimeout
    suspectTimeout = 60
    logAbandoned = true // needed for suspectTimeout logging
    validationQuery = "SELECT 1"
}
}

TestController.groovy:

class TestController {
  def testService

  def test() {
    Integer id = testService.test()
    render "Testing: $id\n"
  }
}  

TestService.groovy:

import grails.transaction.Transactional

@Transactional
class TestService {
    def sql

    @Transactional
    Integer test() {
        def row = sql.firstRow("select id from TestTable where name = 'Test'")
        return row.id
    }
}

泄漏检测消息不显示查询是否从事务服务移动到控制器,我认为这是因为控制器不是事务性的。

如果我将resources.groovy更改为使用未经处理的dataSource版本,它也不会报告泄漏消息:

import groovy.sql.Sql
beans = {
    sql(Sql, ref("dataSourceUnproxied"));
}

深入研究实际的groovy.sql.Sql代码,它在我看来问题与firstRow()打开并使用try {} finally {}块从池中关闭自己的连接这一事实有关。但它获得的连接是通过Spring TransactionAwareConnectionFactoryProxy代理的。

在代理源代码中,我看到:

else if (method.getName().equals("close")) {
    // Handle close method: only close if not within a transaction.                             
    ConnectionFactoryUtils.doReleaseConnection(this.target, this.connectionFactory);
    return null;
}

好吧,如果在交易中,底层连接不会被释放回池中?这对我来说没有多大意义,因为它总是在@Transactional服务方法的事务中,所以连接永远不会被关闭,除非Grails或Spring中的其他地方的代码在提交后关闭()连接()?

1 个答案:

答案 0 :(得分:0)

想出来。

我在JmsTransactionManager中配置了resources.groovy

事实证明我还需要显式添加JDBC事务管理器。我必须将以下内容添加到resources.groovy:

transactionManager(org.springframework.jdbc.datasource.DataSourceTransactionManager, ref("dataSource"))

这为我提供了一个包含Grails的ChainedTransactionManager,其中包含JmsTransactionManagerDataSourceTransactionManager

我没有使用Hibernate。如果使用Hibernate / GORM而不是DataSourceTransactionManager,则需要org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTransactionManager

相关问题