c3p0 连接结帐有时需要 15 分钟才能失败

c3p0 connection checkout taking 15 minutes to fail at times

使用 c3p0 时遇到问题。在大多数情况下工作正常,但在防火墙后面的生产环境中偶尔无法检查连接。问题是需要 15 分钟才能识别连接不可用。池没有耗尽,因为其他连接正在检查并在此 15 分钟的时间间隔内愉快地使用。

日志:

23 Apr 2015 09:08:16.426 [EventProcessor-1] DEBUG c.m.v.c.i.C3P0PooledConnectionPool - Testing PooledConnection [com.mchange.v2.c3p0.impl.NewPooledConnection@5a886282] on CHECKOUT.

15 分钟后:

23 Apr 2015 09:23:43.073 [EventProcessor-1] DEBUG c.m.v.c.i.C3P0PooledConnectionPool - Test of PooledConnection [com.mchange.v2.c3p0.impl.NewPooledConnection@5a886282] on CHECKOUT has FAILED. 
java.sql.SQLException: Connection is invalid
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolPooledConnectionResourcePoolManager.testPooledConnection(C3P0PooledConnectionPool.java:572) [c3p0-0.9.5.jar:0.9.5]
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolPooledConnectionResourcePoolManager.finerLoggingTestPooledConnection(C3P0PooledConnectionPool.java:451) [c3p0-0.9.5.jar:0.9.5]
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolPooledConnectionResourcePoolManager.finerLoggingTestPooledConnection(C3P0PooledConnectionPool.java:443) [c3p0-0.9.5.jar:0.9.5]
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolPooledConnectionResourcePoolManager.refurbishResourceOnCheckout(C3P0PooledConnectionPool.java:336) [c3p0-0.9.5.jar:0.9.5]
    at com.mchange.v2.resourcepool.BasicResourcePool.attemptRefurbishResourceOnCheckout(BasicResourcePool.java:1727) [c3p0-0.9.5.jar:0.9.5]
    at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:553) [c3p0-0.9.5.jar:0.9.5]
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:756) [c3p0-0.9.5.jar:0.9.5]
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:683) [c3p0-0.9.5.jar:0.9.5]
    at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140) [c3p0-0.9.5.jar:0.9.5]

然后是更多日志:

23 Apr 2015 09:23:43.073 [EventProcessor-1] DEBUG c.m.v.r.BasicResourcePool - A resource could not be refurbished for checkout. [com.mchange.v2.c3p0.impl.NewPooledConnection@5a886282] 
java.sql.SQLException: Connection is invalid
...
23 Apr 2015 09:23:43.074 [EventProcessor-1] DEBUG c.m.v.r.BasicResourcePool - Resource [com.mchange.v2.c3p0.impl.NewPooledConnection@5a886282] could not be refurbished in preparation for checkout. Will try to find a better resource. 
23 Apr 2015 09:23:43.074 [C3P0PooledConnectionPoolManager[identityToken->67oy4j981qzvkd716hgow4|4177fc5c]-HelperThread-#2] DEBUG c.m.v.r.BasicResourcePool - Preparing to destroy resource: com.mchange.v2.c3p0.impl.NewPooledConnection@5a886282 
23 Apr 2015 09:23:43.074 [EventProcessor-1] DEBUG c.m.v.c.i.C3P0PooledConnectionPool - Testing PooledConnection [com.mchange.v2.c3p0.impl.NewPooledConnection@41318736] on CHECKOUT. 
23 Apr 2015 09:23:43.074 [C3P0PooledConnectionPoolManager[identityToken->67oy4j981qzvkd716hgow4|4177fc5c]-HelperThread-#2] DEBUG c.m.v.c.i.C3P0PooledConnectionPool - Preparing to destroy PooledConnection: com.mchange.v2.c3p0.impl.NewPooledConnection@5a886282 
23 Apr 2015 09:23:43.076 [C3P0PooledConnectionPoolManager[identityToken->67oy4j981qzvkd716hgow4|4177fc5c]-HelperThread-#2] DEBUG c.m.v.c3p0.impl.NewPooledConnection - Failed to close physical Connection: oracle.jdbc.driver.T4CConnection@25145762 
java.sql.SQLRecoverableException: IO Error: Broken pipe
    at oracle.jdbc.driver.T4CConnection.logoff(T4CConnection.java:612) ~[ojdbc6_g-11.2.0.1.0.jar:11.2.0.1.0]
    at oracle.jdbc.driver.PhysicalConnection.close(PhysicalConnection.java:5094) ~[ojdbc6_g-11.2.0.1.0.jar:11.2.0.1.0]
    at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:642) [c3p0-0.9.5.jar:0.9.5]

c3p0 配置:

        ComboPooledDataSource ods = new ComboPooledDataSource();
...
        ods.setInitialPoolSize(5);
        ods.setMinPoolSize(5);
        ods.setMaxPoolSize(10);
        ods.setMaxStatements(50);

        ods.setTestConnectionOnCheckout(true);

所以没什么特别的。我知道连接丢失是可能的,因此在结账时测试连接。为什么 verify/fail 连接需要这么长时间?我们正在使用 Oracle 数据库。 谢谢。

首先,我假设您已经确认在您的日志消息之间没有对该连接的签出。显然,您会期待很多消息,例如...

Testing PooledConnection [com.mchange.v2.c3p0.impl.NewPooledConnection@5a886282] on CHECKOUT.

...在失败之前的最后一条消息之前。很多这样的消息会出现得更早。理想情况下,只有失败之前的最后一条消息应该比您看到的 15 分钟更接近失败检测。

假设这是最后一条消息,那么问题就与您的 Connections 如何死亡有关。 c3p0 运行s 测试,然后等待成功完成或异常。如果您的连接以连接测试仅挂起 15 分钟的方式终止,那么您可能会看到您所看到的。

这里有一些建议。

  1. 最好在客户端结账之前使用 c3p0 的 idleConnectionTestPeriod 检测这些故障,这样客户端就不太可能遇到长时间挂起。 (您也可以在签到时进行测试。)
  2. 弄清楚正在进行哪种连接测试 运行。您使用的是 c3p0 0.9.5,所以如果您的驱动程序支持它,则默认测试是调用 Connection.isValid(),这应该很快。我在你引用的任何日志中都没有看到实际测试失败的堆栈跟踪(也许它是一个 t运行 的根本原因异常?它肯定会被记录在 FINER/DEBUG 级别一个名为 com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool 的记录器)验证(从堆栈跟踪)您的驱动程序正在使用快速 isValid() 连接测试而不是 c3p0 的慢速默认连接测试。如果不是(可能是因为您的驱动程序不支持),则考虑设置快速 preferredTestQuery.
  3. 您可以尝试 maxAdministrativeTaskTime,但只有当挂起的连接测试响应 interrupt() 调用时,这才可能真正有帮助。

无论如何,我希望这不是完全没用!

看起来是这样一种情况,当连接被防火墙终止时,根本没有任何响应发回,即使是没有数据的 TCP ACK。在这种情况下,验证连接的查询永远不会 return。这是 socket/jdbc 驱动级别。

解决方案:

  • 找出防火墙断开连接策略(在我们的例子中是 1 小时)
  • 设置c3p0.maxConnectionAge属性强制c3p0每X秒重新连接一次。