Tomcat 连接池:少数方法不释放连接
Tomcat Connection pool : few methods not releasing connection
我正在使用 tomcat 连接池。但我正在关注异常
org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object
所以我在 context.xml 中输入以下几行来查找泄漏:
removeAbandoned="true" logAbandoned="true" removeAbandonedTimeout="3"
然后我开始遇到以下异常org.apache.tomcat.dbcp.dbcp.AbandonedTrace$AbandonedObjectException: DBCP object created 2015-01-17 22:12:18 by the following code was never closed:
因此,我找到了导致此泄漏的两个罪魁祸首。这两种方法具有获取连接的通用方式,即调用 unwrap 以访问特定于驱动程序的连接。
try (Connection conn = DataSourceConnectionPool.getConnection().unwrap(OracleConnection.class);
OracleCallableStatement cstmt = (OracleCallableStatement) conn.prepareCall(MIGRATE_ACCOUNT)) {
...
....
)
需要注意的重要一点是,我正在使用 JDK7 中的 try 块,即自动资源管理,所以我不需要 finally 块。连接关闭由 JDK 自动处理。但是为什么这个未包装的连接没有关闭。当我尝试执行以下操作时:
try (Connection poolConn = DataSourceConnectionPool.getConnection();
Connection conn = poolConn.unwrap(OracleConnection.class);
我得到 java.sql.SQLException: Already closed.
那么如何关闭这个连接。我是否必须在不使用 try 块的情况下手动完成?不应该尝试块句柄能够处理这个吗?
这是对连接池的错误使用。你不应该在未包装的连接上调用 close()
。
使用池连接的正常流程是
- 获得
Connection
,池获得一个物理连接并且 return 它被包装在自己的包装器中
- 您使用
Connection
- 您在
Connection
上呼叫 close()
。这实际上并没有 关闭 任何东西,池的包装器拦截了 close()
调用并简单地 return 连接到池。
这是可行的,因为池有一个包装器 class,比如 PoolableConnection
implements Connection
。 PoolableConnection
委托底层连接执行实际工作,但它的实现方式(除其他事项外)close()
不同。这会破坏当前的 PoolableConnection
包装器和 return 底层 Connection
到连接池。例如。
这样,您的程序逻辑可以从 DataSource
获得连接,使用 Connection
,然后使用 close()
,就像正常的未连接的 [=12] =].
正是这种透明性使得连接池如此易于使用。
现在,当您调用 unwrap
时,PooledConnection
允许您访问它的内部、真实的 Connection
委托。
你所做的就是调用close()
委托!
这有两个作用:
- 它不会在
PooledConnection
上调用 close()
,因此 Connection
不会 return 进入池。
- 它从池下面关闭底层连接。这应该不是问题,因为池会自行处理断开的连接。
所以你需要非常小心。 总是 调用 close()
你从池中得到的 Connection
,return 到池中。 从不 在基础连接上调用 close()
。
所以你的代码应该是:
try (final Connection poolConn = DataSourceConnectionPool.getConnection()) {
final Connection conn = poolConn.unwrap(OracleConnection.class);
//do stuff with conn
//do not close conn!!
}
//poolConn is returned to the pool
我正在使用 tomcat 连接池。但我正在关注异常
org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object
所以我在 context.xml 中输入以下几行来查找泄漏:
removeAbandoned="true" logAbandoned="true" removeAbandonedTimeout="3"
然后我开始遇到以下异常org.apache.tomcat.dbcp.dbcp.AbandonedTrace$AbandonedObjectException: DBCP object created 2015-01-17 22:12:18 by the following code was never closed:
因此,我找到了导致此泄漏的两个罪魁祸首。这两种方法具有获取连接的通用方式,即调用 unwrap 以访问特定于驱动程序的连接。
try (Connection conn = DataSourceConnectionPool.getConnection().unwrap(OracleConnection.class);
OracleCallableStatement cstmt = (OracleCallableStatement) conn.prepareCall(MIGRATE_ACCOUNT)) {
...
....
)
需要注意的重要一点是,我正在使用 JDK7 中的 try 块,即自动资源管理,所以我不需要 finally 块。连接关闭由 JDK 自动处理。但是为什么这个未包装的连接没有关闭。当我尝试执行以下操作时:
try (Connection poolConn = DataSourceConnectionPool.getConnection();
Connection conn = poolConn.unwrap(OracleConnection.class);
我得到 java.sql.SQLException: Already closed.
那么如何关闭这个连接。我是否必须在不使用 try 块的情况下手动完成?不应该尝试块句柄能够处理这个吗?
这是对连接池的错误使用。你不应该在未包装的连接上调用 close()
。
使用池连接的正常流程是
- 获得
Connection
,池获得一个物理连接并且 return 它被包装在自己的包装器中 - 您使用
Connection
- 您在
Connection
上呼叫close()
。这实际上并没有 关闭 任何东西,池的包装器拦截了close()
调用并简单地 return 连接到池。
这是可行的,因为池有一个包装器 class,比如 PoolableConnection
implements Connection
。 PoolableConnection
委托底层连接执行实际工作,但它的实现方式(除其他事项外)close()
不同。这会破坏当前的 PoolableConnection
包装器和 return 底层 Connection
到连接池。例如。
这样,您的程序逻辑可以从 DataSource
获得连接,使用 Connection
,然后使用 close()
,就像正常的未连接的 [=12] =].
正是这种透明性使得连接池如此易于使用。
现在,当您调用 unwrap
时,PooledConnection
允许您访问它的内部、真实的 Connection
委托。
你所做的就是调用close()
委托!
这有两个作用:
- 它不会在
PooledConnection
上调用close()
,因此Connection
不会 return 进入池。 - 它从池下面关闭底层连接。这应该不是问题,因为池会自行处理断开的连接。
所以你需要非常小心。 总是 调用 close()
你从池中得到的 Connection
,return 到池中。 从不 在基础连接上调用 close()
。
所以你的代码应该是:
try (final Connection poolConn = DataSourceConnectionPool.getConnection()) {
final Connection conn = poolConn.unwrap(OracleConnection.class);
//do stuff with conn
//do not close conn!!
}
//poolConn is returned to the pool