这是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中的其他地方的代码在提交后关闭()连接()?
答案 0 :(得分:0)
想出来。
我在JmsTransactionManager
中配置了resources.groovy
。
事实证明我还需要显式添加JDBC事务管理器。我必须将以下内容添加到resources.groovy:
transactionManager(org.springframework.jdbc.datasource.DataSourceTransactionManager, ref("dataSource"))
这为我提供了一个包含Grails的ChainedTransactionManager
,其中包含JmsTransactionManager
和DataSourceTransactionManager
。
我没有使用Hibernate。如果使用Hibernate / GORM而不是DataSourceTransactionManager
,则需要org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTransactionManager
。