J'ai une application Grails qui a des rafales d'activité élevée, mais souvent des périodes d'inactivité qui peuvent durer plusieurs heures ou toute la nuit. Je remarque que les premiers utilisateurs du matin obtiennent le type d'exception suivant, et je pense que cela est dû aux connexions dans le pool qui deviennent obsolètes et à la fermeture de la base de données MYSql.
J'ai trouvé des informations contradictoires dans Google pour savoir si l'utilisation de la propriété de connexion Connecteur/J 'autoReconnect = true' est une bonne idée (et si le client recevra toujours une exception même si la connexion est ensuite rétablie), ou s'il faut définir d'autres propriétés qui vont périodiquement expulser ou rafraîchir les connexions inactives, les tester lors de l'emprunt, etc. Grails utilise DBCP en dessous. J'ai actuellement une configuration simple comme ci-dessous, et je cherche une réponse sur la meilleure façon de garantir que toute connexion récupérée hors du pool après une longue période d'inactivité est valide et non fermée.
dataSource {
pooled = true
dbCreate = "update"
url = "jdbc:mysql://my.ip.address:3306/databasename"
driverClassName = "com.mysql.jdbc.Driver"
dialect = org.hibernate.dialect.MySQL5InnoDBDialect
username = "****"
password = "****"
properties {
//what should I add here?
}
}
Exception
2012-06-20 08:40:55,150 [http-bio-8443-exec-1] ERROR transaction.JDBCTransaction - JDBC begin failed
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 64,129,968 milliseconds ago. The last packet sent successfully to the server was 64,129,968 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
at com.mysql.jdbc.Util.handleNewInstance(Util.Java:411)
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.Java:1116)
at com.mysql.jdbc.MysqlIO.send(MysqlIO.Java:3851)
...... Lots more .......
Caused by: Java.sql.SQLException: Already closed.
at org.Apache.commons.dbcp.PoolableConnection.close(PoolableConnection.Java:114)
Le plus simple consiste à configurer le pool de connexions pour spécifier la requête à exécuter pour tester la connexion avant de la transmettre à l'application:
validationQuery="select 1 as dbcp_connection_test"
testOnBorrow=true
Cette même requête "validation de connexion" peut être exécutée sur d'autres événements. Je ne suis pas sûr des valeurs par défaut pour celles-ci:
testOnReturn=true
testWhileIdle=true
Il existe également des paramètres de configuration qui limitent l '"âge" des connexions inactives dans le pool, ce qui peut être utile si les connexions inactives sont fermées du côté serveur.
minEvictableIdleTimeMillis
timeBetweenEvictionRunsMillis
Je ne sais pas si c'est la meilleure façon de gérer la connexion à la base de données, mais j'ai eu les mêmes problèmes que vous avez décrits. J'ai beaucoup essayé et je me suis retrouvé avec le pool de connexions c3p .
En utilisant c3p0, vous pouvez forcer votre application à actualiser la connexion à la base de données après un certain temps.
Placer le c3p0.jar
dans votre dossier lib
et ajoutez votre configuration à conf/spring/resources.groovy
.
Ma resources.groovy
ressemble à ça:
import com.mchange.v2.c3p0.ComboPooledDataSource
import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH
beans = {
/**
* c3P0 pooled data source that forces renewal of DB connections of certain age
* to prevent stale/closed DB connections and evicts excess idle connections
* Still using the JDBC configuration settings from DataSource.groovy
* to have easy environment specific setup available
*/
dataSource(ComboPooledDataSource) { bean ->
bean.destroyMethod = 'close'
//use grails' datasource configuration for connection user, password, driver and JDBC url
user = CH.config.dataSource.username
password = CH.config.dataSource.password
driverClass = CH.config.dataSource.driverClassName
jdbcUrl = CH.config.dataSource.url
//force connections to renew after 4 hours
maxConnectionAge = 4 * 60 * 60
//get rid too many of idle connections after 30 minutes
maxIdleTimeExcessConnections = 30 * 60
}
}