Le pool Java c3p0 se déconnecte après 5 à 10 minutes de veille


Dans mon application Javafx, je me connecte au serveur distant sur Hetzner.de utilisation de sftp. Pour gérer les connexions, j'utilise le pool de connexions de bibliothèque cp30 avec les paramètres suivants:

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;
}

La connexion va bien. Le journal de la console pour la connexion est:

ноя 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 ]

Mais si l'interface graphique reste en attente pendant différentes périodes de temps, j'obtiens une erreur:

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

Bien sûr, je ferme les instructions et les ResultSets, en utilisant try with resources:

try (Statement stmt = m.conSayt.createStatement(); ResultSet rs = stmt.executeQuery(SQL);) {
    // Contents.
} catch (SQLException ex){
    // Contents.
}
Author: Zon, 2013-11-10

1 answers

Le problème ici est la façon dont vous utilisez (abuser, j'ai peur) c3p0.

Chaque fois que vous obtenez une connexion, vous créez un nouveau pool de connexions, puis démarrez le pool de connexions, puis le laissez hors de portée. Notez que dans vos journaux, vous initialisez trois pools c3p0 différents en trois secondes, une fois chaque fois que vous essayez d'acquérir une connexion. Pas bonne.

Vous ne nettoyez jamais votre Connexion. (Vous ne nettoyez pas réellement les ResultSets et les instructions dans le code que vous fournir ici.) Comme le dit samlewis, il semble que vous ayez l'intention de maintenir la connexion ouverte.

Tout cela n'est pas du tout la façon dont vous devez utiliser un pool de connexions. Si vous souhaitez récupérer une connexion et la maintenir ouverte, récupérez simplement la connexion directement à l'aide de DriverManager.getConnection( ... ) et d'éviter tout un tas de complexité.

Sauf que vous constaterez, comme vous l'avez déjà trouvé, que maintenir une connexion JDBC ouverte pendant de longues périodes et attendre de la réutiliser est un moyen fragile pour exécuter une application. C'est le problème que les pools de connexion existent pour résoudre.

Avec un pool de connexions, les choses doivent être différentes:

1) Que ComboPooledDataSource vous créez? Ne le laissez pas disparaître. Tenir sur une référence à quelque part, peut-être comme un champ statique, peut-être en tant que membre d'un Objet que vous utilisez. Vous devez créer uneDataSource une heure pour un déploiement d'application typique.

2) Chaque fois que vous avez besoin d'une connexion i) acquérir un " nouveau" Connexion en appelant getConnection () sur la source de données; ii) faites le travail que vous devez faire; iii) fermez () cette connexion immédiatement après utilisation.

C3p0 maintiendra un pool de connexions, donc tout cela sera rapide. la cpds.getConnection () ne fera pas de connexions réseau au SGBD pour établir une nouvelle connexion, il vous remettra simplement une connexion à partir du pool. Lorsque vous fermez() la connexion, la Connexion au sgbd ne sera détruite, juste recyclés dans la piscine. Il s'agit de la mise en commun de connexions transparentes standard JDBC.

[Oups! Je n'ai pas remarqué, le questionneur utilise java 7 try-with-resources, qui devrait être fina, tant que les connexions sont parmi les ressources!] Notez que l'appel de fonctions qui créent des ResultSets, des Instructions ou des Connnections dans un bloc try {} n'est pas du tout suffisant pour garantir que ces ressources sont close()ed. Vous devez réellement les fermer, dans un bloc enfin:

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(); }
}

Encore une fois, les DPC ne devraient pas être variable locale. Votre Connexion, en revanche, doit être fermée (généralement) dans le cadre de la méthode qui l'a ouverte, dans un bloc finally.

[Notez que chaque ressource close() à l'intérieur du bloc finally est encapsulée dans un try imbriqué, pour s'assurer qu'une Exception dans one close() n'empêche pas les autres ressources d'une meilleure tentative close(). Vous pouvez rendre ce nettoyeur en enveloppant les essais imbriqués dans les méthodes d'assistance.]

Quelques autres commentaires: acquireIncrement de 1 est généralement un mauvaise idée, il y a peu de raisons de définir testConnectionOnCheckin sur true si vous testez déjà des connexions lors du paiement. Mais pour le moment, rien de tout cela n'a d'importance, la seule configuration que votre application exerce réellement est le jdbcUrl, l'utilisateur et le mot de passe, car vous n'obtenez qu'une seule connexion, puis jetez (enfin, essayez de jeter) le pool.

Il vaut la peine d'expliquer que "essayer de jeter". les pools c3p0 engendrent leurs propres Threads de maintenance. Comme vous construisez et initialisez de nouveaux pools, puis laissez-les sortir de la portée sans les fermer (), vous créez des Threads et une fuite de mémoire. Chaque fois que vous avez terminé avec un ComboPooledDataSource c3p0, vous devez appeler close() [ou plus rarement la méthode statique DataSources.détruire( ... )] pour fermer la source de données vers le bas. En règle générale, cela se fait une fois, car une application s'arrête ou se réinitialise.

 5
Author: Steve Waldman, 2013-11-11 19:22:10