2012-06-20 6 views
33

Ich habe eine Grails-App, die hohe Aktivität hat, aber dann oft Perioden der Inaktivität, die dauern kann mehrere Stunden bis über Nacht. Ich stelle fest, dass die ersten Benutzer am Morgen die folgende Art von Ausnahme erhalten, und ich glaube, dass dies aufgrund der Verbindungen in dem Pool, der veraltet ist, und der MYSQL-Datenbank, die sie schließt, ist.Richtiger Weg, um gepoolte Verbindungen während längerer Inaktivität für MySQL aufrechtzuerhalten, Grails 2 App

Ich habe in Googling widersprüchliche Informationen darüber gefunden, ob die Verwendung der Connector/J-Verbindungseigenschaft 'autoReconnect = true' sinnvoll ist (und ob der Client immer noch eine Ausnahme erhält, selbst wenn die Verbindung wiederhergestellt wird). oder ob andere Eigenschaften eingestellt werden sollen, die inaktive Verbindungen periodisch löschen oder auffrischen, auf Ausleihungen usw. testen. Grails verwendet DBCP darunter. Ich habe derzeit eine einfache Konfiguration wie unten, und bin auf der Suche nach einer Antwort darauf, wie man am besten sicherstellen, dass jede Verbindung, die aus dem Pool nach einer langen inaktiven Periode ergriffen wurde, gültig und nicht geschlossen ist.

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) 

Antwort

29

Am einfachsten ist es, den Verbindungspool zu konfigurieren, dass die Abfrage angeben, ausgeführt werden, um die Verbindung zu testen, bevor sie an die Anwendung übergeben wird:

validationQuery="select 1 as dbcp_connection_test" 
testOnBorrow=true 

Diese Die gleiche "Verbindungsvalidierung" -Abfrage kann für andere Ereignisse ausgeführt werden. Ich bin nicht sicher, ob der Standardwertes für diese:

testOnReturn=true 
testWhileIdle=true 

Es gibt auch Konfigurationseinstellungen, die das „Alter“ inaktiver Verbindungen im Pool zu begrenzen, was nützlich sein kann, wenn Verbindungen im Leerlauf auf der Serverseite geschlossen werden .

minEvictableIdleTimeMillis 
timeBetweenEvictionRunsMillis 

http://commons.apache.org/dbcp/configuration.html

+0

Danke für den Link, irgendwie verpasst die Seite, indem Sie direkt auf die javadocs. Ich werde eine Kombination von diesen implementieren und sehen, wie es funktioniert. – Peter

+0

Leider hat DBCP immer noch keine Konfiguration, um das Alter der Verbindung oder die Anzahl der Verbindungsversuche zu begrenzen, bevor sie aus dem Pool entfernt wird. (Andere Implementierungen von Verbindungspools tun dies.) – spencer7593

+0

obwohl, Spencer7593, das sollte keine Rolle spielen, wenn die Verbindungen verwendet werden, oder geräumt/aktualisiert, wenn sie durch Inaktivität schlecht werden, denke ich, da es keinen Einfluss auf den Benutzer hat. – Peter

7

Ich weiß nicht, ob es der beste Weg ist, die Datenbankverbindung zu handhaben, aber ich hatte die gleichen Probleme wie Sie desribed. Ich habe viel versucht und bin mit der c3p0 connection pool gelandet.

Mit c3p0 können Sie Ihre App erzwingen, die Datenbankverbindung nach einer bestimmten Zeit zu aktualisieren.

Platzieren Sie die c3p0.jar in Ihrem Ordner lib und fügen Sie Ihre Konfiguration zu conf/spring/resources.groovy hinzu.

Mein resources.groovy sieht wie folgt aus:

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 
    } 
} 
+0

+1 Einige Verbindungspoolimplementierungen bieten robustere Funktionen als DBCP. Neuere Versionen von DBCP unterstützen die Räumung, wenn "maximale Leerlaufzeit" verstrichen ist, aber DBCP fehlen noch andere Konfigurationsoptionen, wie etwa das maximale Alter und die maximale Anzahl an Verwendungen. – spencer7593

+1

Ich habe gefunden, dass C3PO wirklich gut funktioniert, wenn ich Probleme mit der Verbindungszeitüberschreitung bei Grails-Stacks habe. –