Java c3p0池待机5-10分钟后断开连接

时间:2013-11-10 16:26:02

标签: java javafx c3p0

在我的Javafx应用程序中,我使用sftp连接到Hetzner.de上的远程服务器。为了管理连接,我使用cp30库连接池和以下参数:

public Connection dbConnectSite() throws SQLException, PropertyVetoException {
    ComboPooledDataSource cpds = new ComboPooledDataSource();
    cpds.setDriverClass("com.mysql.jdbc.Driver");
    cpds.setJdbcUrl("jdbc:mysql://" + mySQLHost + ":" + mySQLPort + "/" + mySQLDBName + "?characterEncoding=UTF-8&autoReconnect=true"); // 192.168.100.100 v seti.
    cpds.setUser(mySQLUser);
    cpds.setPassword(mySQLPassword);
    cpds.setMinPoolSize(3);
    cpds.setMaxPoolSize(20); // Maximum number of Connections a pool will maintain at any given time.
    cpds.setAcquireIncrement(1);
    cpds.setTestConnectionOnCheckin(true); // If true, an operation will be performed asynchronously at every connection checkin to verify that the connection is valid.
    cpds.setTestConnectionOnCheckout(true); // If true, an operation will be performed at every connection checkout to verify that the connection is valid.
    cpds.setIdleConnectionTestPeriod(300); // If this is a number greater than 0, c3p0 will test all idle, pooled but unchecked-out connections, every this number of seconds.
    cpds.setMaxIdleTimeExcessConnections(240);
    cpds.setMaxIdleTime(3600); // Seconds a Connection can remain pooled but unused before being discarded. Zero means idle connections never expire.
    cpds.setMaxStatements(100);
    cpds.setCheckoutTimeout(0); // The number of milliseconds a client calling getConnection() will wait for a Connection to be checked-in or acquired when the pool is exhausted. Zero means wait indefinitely.
    cpds.setMaxAdministrativeTaskTime(0); // Seconds before c3p0's thread pool will try to interrupt an apparently hung task. 
    cpds.setMaxConnectionAge(saytPort); // Seconds, effectively a time to live. A Connection older than maxConnectionAge will be destroyed and purged from the pool. Zero means no maximum absolute age is enforced. 
    cpds.setPreferredTestQuery("SELECT 1");
    dsSite = cpds;
    conSayt = dsSite.getConnection();
    return conSayt;
}

连接正常。连接的控制台日志是:

ноя 10, 2013 9:59:47 PM com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
ноя 10, 2013 9:59:47 PM com.mchange.v2.c3p0.C3P0Registry banner
INFO: Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10]
ноя 10, 2013 9:59:47 PM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 1, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> z8kfsx8yiing6p1lp8nb2|3ba701c9, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> z8kfsx8yiing6p1lp8nb2|3ba701c9, idleConnectionTestPeriod -> 300, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://sql249.your-server.de:3306/kombinezonik?characterEncoding=UTF-8&autoReconnect=true, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 22, maxIdleTime -> 3600, maxIdleTimeExcessConnections -> 240, maxPoolSize -> 20, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> SELECT 1, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> true, testConnectionOnCheckout -> true, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
nov 10, 2013 9:59:49 PM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> z8kfsx8yiing6p1lp8nb2|2bc60511, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> z8kfsx8yiing6p1lp8nb2|2bc60511, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/kombadmin?characterEncoding=UTF-8&autoReconnect=true, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]

但是如果GUI在某些不同的时间段待命,我会收到一个错误:

nov 10, 2013 10:07:50 PM MyClass
SEVERE: null
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 344 144 milliseconds ago.  The last packet sent successfully to the server was 2 milliseconds ago.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1117)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3603)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3492)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4043)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2503)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2788)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2738)
    at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1617)
    at com.mchange.v2.c3p0.impl.NewProxyStatement.executeQuery(NewProxyStatement.java:35)
    at MyClass.java:158
    at MyClass$1$1$1.run(MyClass.java:128)
    at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:179)
    at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:176)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:176)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:76)
    at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
    at com.sun.glass.ui.gtk.GtkApplication$3$1.run(GtkApplication.java:82)
    at java.lang.Thread.run(Thread.java:722)
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3052)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3503)
    ... 18 more

当然,我使用try with resources:

关闭Statements和ResultSet
try (Statement stmt = m.conSayt.createStatement(); ResultSet rs = stmt.executeQuery(SQL);) {
    // Contents.
} catch (SQLException ex){
    // Contents.
}

1 个答案:

答案 0 :(得分:5)

这里的问题是你使用的方式(误用,我害怕)c3p0。

每次获得Connection时,您将创建一个新的连接池,然后启动连接池,然后让它超出范围。请注意,在日志中,您将在三秒内初始化三个不同的c3p0池,每次尝试获取一个Connection时。不好。

您永远不会清理您的连接。 (您实际上并没有在此处提供的代码中清理ResultSet和Statements。)正如samlewis所说,看起来您打算只打开Connection。

所有这些根本不是你应该如何使用连接池。如果你想获取一个Connection并保持打开状态,只需使用DriverManager.getConnection(...)直接获取Connection,并避免很多复杂性。

除非您发现,正如您已经发现的那样,长时间打开JDBC连接并期望重用它是运行应用程序的一种脆弱方式。这就是连接池存在要解决的问题。

使用连接池时,内容必须不同:

1)你创建的ComboPooledDataSource?不要让它消失。在某处继续引用它,可能作为静态字段,也许作为您使用的某个Object的成员。您应为典型的应用程序部署创建一个 DataSource 一个时间。

2)每次需要连接时i)通过调用DataSource上的getConnection()获取“新”连接; ii)做你需要做的任何工作; iii) close()使用后立即连接。

c3p0将维护一个连接池,所以这一切都会很快。 cpds.getConnection()不会建立到dbms的网络连接以建立新的Connection,它只会从池中传递一个Connection。当你关闭()连接时,与dbms的连接实际上不会被销毁,只是回收到池中。这是JDBC标准的透明连接池。

<强> [糟糕!我没有注意到,提问者使用java 7 try-with-resources,这应该是fina,只要Connections属于资源!] 请注意调用函数创建ResultSet,Statements或Connnections in try {}块根本不足以确保这些资源是close()ed。你必须在最后一个块中实际关闭它们:

Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try
{
    con = cpds.getConnection();
    stmt = cpds.createStatement();
    rs = stmt.executeQuery("SELECT something FROM somewhere");
    while ( rs.next() )
    {
       // do some stuff with results
    }
}
finally
{
   try { if (rs != null) rs.close() } catch (SQLException e) { e.printStackTrace(); }
   try { if (stmt != null) stmt.close() } catch (SQLException e) { e.printStackTrace(); }
   try { if (con != null) con.close() } catch (SQLException e) { e.printStackTrace(); }
}

同样,cpds不应该是局部变量。另一方面,您的Connection应该在(最终)块中打开它的方法范围内(通常)关闭。

[请注意,finally块中的每个资源close()都包含在嵌套的try中,以确保一个close()内的Exception不会阻止其他资源进行最佳尝试close()。您可以通过将嵌套尝试包装在辅助方法中来使其更清晰。]

其他一些注释:acquireIncrement为1通常是一个坏主意,如果你已经在结账时测试了Connections,那么没有理由将testConnectionOnCheckin设置为true。但就目前而言,这一切都不重要,您的应用程序实际执行的唯一配置是jdbcUrl,用户和密码,因为您只获得一个连接,然后丢弃(好吧,试图丢弃)池。 / p>

值得解释的是“试图丢弃”。 c3p0池产生自己的维护线程。在构造和初始化新池然后让它们超出范围而不关闭它们时,您正在创建线程和内存泄漏。无论何时完成c3p0 ComboPooledDataSource,都必须调用close()[或更少的静态方法DataSources.destroy(...)]来关闭DataSource。通常这样做一次,因为应用程序正在关闭或重置自身。